ksmaru's picture
Update app.py
e604264 verified
Raw
History Blame Contribute Delete
7.15 kB
import gradio as gr
import joblib
import pandas as pd
import numpy as np
import shap
import matplotlib.pyplot as plt
from fpdf import FPDF
import tempfile
# --- Загрузка моделей ---
try:
model = joblib.load('allostatic_model.pkl')
imputer = joblib.load('imputer.pkl')
explainer = joblib.load('shap_explainer.pkl')
expected_features = imputer.feature_names_in_.tolist()
except Exception as e:
print(f"Ошибка загрузки: {e}")
RU_NAMES = {
'RIDAGEYR': 'Возраст', 'RIAGENDR': 'Пол', 'BMXBMI': 'ИМТ (BMI)',
'BPXSY1': 'Сист. давление', 'LBXNEPCT': 'Нейтрофилы %', 'LBDLYMNO': 'Лимфоциты %',
'LBXSATSI': 'АСТ (U/L)', 'LBXPLTSI': 'Тромбоциты', 'LBXSCR': 'Креатинин',
'LBXGH': 'Гликированный Hb', 'RXDCOUNT': 'Кол-во лекарств', 'NLR': 'Индекс NLR',
'APRI': 'Индекс APRI', 'eGFR': 'eGFR (фильтрация)', 'has_symptoms_for_stage4': 'Симптомы',
'simplified_AL_Index': 'Аллостатический индекс'
}
def calculate_metrics_dict(d):
g_num = 1 if d.get('RIAGENDR') == "Male" else 2
age = float(d.get('RIDAGEYR', 50))
creat = float(d.get('LBXSCR', 0.9))
nlr = d.get('LBXNEPCT', 60) / d.get('LBDLYMNO', 30) if d.get('LBDLYMNO', 0) > 0 else 0
apri = (d.get('LBXSATSI', 25) / 40) / d.get('LBXPLTSI', 250) * 100 if d.get('LBXPLTSI', 0) > 0 else 0
kappa = 0.7 if g_num == 2 else 0.9
alpha = -0.241 if g_num == 2 else -0.302
egfr = 142 * (min(creat/kappa, 1)**alpha) * (max(creat/kappa, 1)**-1.2) * (0.9938**age) * (1.012 if g_num == 2 else 1.0)
symptoms = 1 if any([d.get('PFQ', False), d.get('MEM', False), d.get('CONC', False), d.get('WALK', False)]) else 0
al_idx = sum([nlr > 2.5, d.get('LBXGH', 5.4) > 5.7, egfr < 90, d.get('RXDCOUNT', 0) >= 3, apri > 0.5, d.get('BMXBMI', 25) > 30, d.get('BPXSY1', 120) > 140])
mapping = {
'RIDAGEYR': age, 'RIAGENDR': g_num, 'BMXBMI': d.get('BMXBMI', 25),
'BPXSY1': d.get('BPXSY1', 120), 'LBXNEPCT': d.get('LBXNEPCT', 60), 'LBDLYMNO': d.get('LBDLYMNO', 30),
'LBXSATSI': d.get('LBXSATSI', 25), 'LBXPLTSI': d.get('LBXPLTSI', 250), 'LBXSCR': creat, 'LBXGH': d.get('LBXGH', 5.4),
'RXDCOUNT': d.get('RXDCOUNT', 0), 'NLR': nlr, 'APRI': apri, 'eGFR': egfr,
'has_symptoms_for_stage4': symptoms, 'simplified_AL_Index': al_idx,
'PFQ049': 1 if d.get('PFQ') else 0, 'PFQ051': 1 if d.get('PFQ') else 0,
'PFQ061Q': 1 if d.get('PFQ') else 0, 'PFQ090': 1 if d.get('PFQ') else 0
}
return mapping, al_idx, egfr
def main_analysis(age, gender, bmi, sys_bp, neutro, lympho, ast, plt_val, creat, hba1c, rxd, pfq, mem, conc, walk):
input_data = {
'RIDAGEYR': age, 'RIAGENDR': gender, 'BMXBMI': bmi, 'BPXSY1': sys_bp,
'LBXNEPCT': neutro, 'LBDLYMNO': lympho, 'LBXSATSI': ast, 'LBXPLTSI': plt_val,
'LBXSCR': creat, 'LBXGH': hba1c, 'RXDCOUNT': rxd, 'PFQ': pfq, 'MEM': mem, 'CONC': conc, 'WALK': walk
}
mapping, al_idx, egfr = calculate_metrics_dict(input_data)
input_df = pd.DataFrame([{f: mapping.get(f, 0) for f in expected_features}])[expected_features]
input_imputed = imputer.transform(input_df)
raw_stage = int(model.predict(input_imputed)[0])
stage = 1 if al_idx <= 1 and raw_stage >= 5 else raw_stage
# SHAP
plt.close('all')
plt.figure(figsize=(10, 5))
s_vals = explainer.shap_values(input_imputed)
s_array = np.array(s_vals)
c_idx = min(stage, s_array.shape[0]-1) if s_array.ndim == 3 else 0
final_shap = s_array[c_idx, 0, :] if s_array.ndim == 3 else (s_array[0] if s_array.ndim > 1 else s_array)
shap.bar_plot(final_shap, feature_names=[RU_NAMES.get(f, f) for f in expected_features], show=False)
plt.tight_layout()
plot_path = tempfile.NamedTemporaryFile(delete=False, suffix=".png").name
plt.savefig(plot_path)
# PDF
pdf = FPDF()
pdf.add_page()
pdf.set_font("Arial", 'B', size=16)
pdf.cell(200, 10, txt="Allostatic Risk Report", ln=True, align='C')
pdf.set_font("Arial", size=12)
pdf.ln(10)
pdf.cell(200, 10, txt=f"Stage: {stage}", ln=True)
pdf.cell(200, 10, txt=f"AL Index: {al_idx}", ln=True)
pdf.cell(200, 10, txt=f"eGFR: {egfr:.2f}", ln=True)
pdf.image(plot_path, x=10, y=70, w=180)
pdf_path = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf").name
pdf.output(pdf_path)
# Цветовая индикация
color = "green" if stage == 1 else "orange" if stage < 4 else "red"
res_label = f"СТАДИЯ {stage}"
return res_label, f"Индекс нагрузки: {al_idx} | eGFR: {egfr:.1f}", plot_path, pdf_path
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("# 🏥 ProPharm Dashboard")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### 🎚️ Параметры пациента")
with gr.Group():
age = gr.Slider(18, 95, label="Возраст", value=50)
gender = gr.Radio(["Male", "Female"], label="Пол", value="Male")
bmi = gr.Slider(10, 60, label="ИМТ (BMI)", value=24.5, step=0.1)
sys_bp = gr.Slider(80, 220, label="Систолическое АД", value=120)
with gr.Group():
gr.Markdown("🧪 **Лаборатория**")
neutro = gr.Slider(10, 90, label="Нейтрофилы %", value=60)
lympho = gr.Slider(5, 70, label="Лимфоциты %", value=30)
ast = gr.Slider(5, 150, label="АСТ (U/L)", value=25)
plt_val = gr.Slider(50, 600, label="Тромбоциты", value=250)
creat = gr.Slider(0.3, 5.0, label="Креатинин (mg/dL)", value=0.9, step=0.1)
hba1c = gr.Slider(3, 15, label="Гликированный Hb %", value=5.4, step=0.1)
rxd = gr.Slider(0, 15, label="Кол-во лекарств", value=0)
with gr.Group():
gr.Markdown("🧠 **Гериатрический статус**")
with gr.Row():
pfq = gr.Checkbox(label="Физ. огр.")
mem = gr.Checkbox(label="Память")
conc = gr.Checkbox(label="Конц.")
walk = gr.Checkbox(label="Ходьба")
btn = gr.Button("🚀 ПРОВЕСТИ АНАЛИЗ", variant="primary")
with gr.Column(scale=1):
gr.Markdown("### 📊 Заключение")
out_stage = gr.Label(label="Результат")
out_ind = gr.Textbox(label="Ключевые показатели")
out_plot = gr.Image(label="Вклад факторов (SHAP)")
out_pdf = gr.File(label="📥 Отчет для печати (PDF)")
btn.click(main_analysis, [age, gender, bmi, sys_bp, neutro, lympho, ast, plt_val, creat, hba1c, rxd, pfq, mem, conc, walk], [out_stage, out_ind, out_plot, out_pdf])
demo.launch()