| 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 |
| |
| |
| 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 = 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() |