|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
import pandas as pd
|
|
|
import numpy as np
|
|
|
import gradio as gr
|
|
|
from datetime import datetime
|
|
|
import warnings
|
|
|
warnings.filterwarnings('ignore')
|
|
|
|
|
|
|
|
|
try:
|
|
|
import numpy as np
|
|
|
print("โ
numpy imported successfully")
|
|
|
except ImportError:
|
|
|
print("โ numpy not available - installing...")
|
|
|
os.system("pip install numpy==1.26.4")
|
|
|
import numpy as np
|
|
|
|
|
|
try:
|
|
|
import joblib
|
|
|
print("โ
joblib imported successfully")
|
|
|
except ImportError:
|
|
|
print("โ joblib not available - installing...")
|
|
|
os.system("pip install joblib")
|
|
|
import joblib
|
|
|
|
|
|
try:
|
|
|
import json
|
|
|
print("โ
json imported successfully")
|
|
|
except ImportError:
|
|
|
print("โ json not available")
|
|
|
|
|
|
try:
|
|
|
import pandas as pd
|
|
|
print("โ
pandas imported successfully")
|
|
|
except ImportError:
|
|
|
print("โ pandas not available - installing...")
|
|
|
os.system("pip install pandas")
|
|
|
import pandas as pd
|
|
|
|
|
|
print("๐ Initializing Hospital Readmission Predictor for Hugging Face Spaces...")
|
|
|
|
|
|
|
|
|
MODEL_PATH = "models/production_model.pkl"
|
|
|
PREPROCESSOR_PATH = "models/smoteenn_preprocessor.pkl"
|
|
|
MODEL_INFO_PATH = "models/model_info.json"
|
|
|
|
|
|
print(f"๐ Looking for model files...")
|
|
|
print(f"๐ค Model file exists: {os.path.exists(MODEL_PATH)}")
|
|
|
print(f"โ๏ธ Preprocessor file exists: {os.path.exists(PREPROCESSOR_PATH)}")
|
|
|
print(f"๐ Model info file exists: {os.path.exists(MODEL_INFO_PATH)}")
|
|
|
|
|
|
|
|
|
model = None
|
|
|
preprocessor = None
|
|
|
model_info = None
|
|
|
|
|
|
try:
|
|
|
if os.path.exists(MODEL_PATH):
|
|
|
print("๐ฅ Loading production model...")
|
|
|
|
|
|
try:
|
|
|
import numpy as np
|
|
|
|
|
|
np.random.seed(42)
|
|
|
except:
|
|
|
print("โ ๏ธ Numpy issue detected, attempting fix...")
|
|
|
|
|
|
model = joblib.load(MODEL_PATH)
|
|
|
print("โ
Production model loaded successfully!")
|
|
|
else:
|
|
|
print("โ ๏ธ Model file not found - using demo mode")
|
|
|
|
|
|
if os.path.exists(PREPROCESSOR_PATH):
|
|
|
print("๐ฅ Loading preprocessor...")
|
|
|
preprocessor = joblib.load(PREPROCESSOR_PATH)
|
|
|
print("โ
Preprocessor loaded successfully!")
|
|
|
else:
|
|
|
print("โ ๏ธ Preprocessor file not found")
|
|
|
|
|
|
if os.path.exists(MODEL_INFO_PATH):
|
|
|
print("๐ฅ Loading model information...")
|
|
|
with open(MODEL_INFO_PATH, 'r') as f:
|
|
|
model_info = json.load(f)
|
|
|
print("โ
Model information loaded successfully!")
|
|
|
print(f"๐ฏ Model Accuracy: {model_info['accuracy']:.2%}")
|
|
|
print(f"๐ AUC Score: {model_info['auc']:.4f}")
|
|
|
else:
|
|
|
print("โ ๏ธ Model info file not found - using default info")
|
|
|
model_info = {
|
|
|
"model_type": "RandomForest Classifier",
|
|
|
"accuracy": 0.7471,
|
|
|
"auc": 0.827,
|
|
|
"feature_count": 62,
|
|
|
"training_samples": 12236,
|
|
|
"training_technique": "SMOTEENN",
|
|
|
"created_date": "2025-09-10"
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
print(f"โ Error loading models: {e}")
|
|
|
print("๐ Running in demo mode...")
|
|
|
|
|
|
if model_info is None:
|
|
|
model_info = {
|
|
|
"model_type": "RandomForest Classifier (Demo Mode)",
|
|
|
"accuracy": 0.7471,
|
|
|
"auc": 0.827,
|
|
|
"feature_count": 62,
|
|
|
"training_samples": 12236,
|
|
|
"training_technique": "SMOTEENN",
|
|
|
"created_date": "2025-09-10"
|
|
|
}
|
|
|
|
|
|
class HospitalReadmissionPredictor:
|
|
|
def __init__(self, model, preprocessor, model_info):
|
|
|
self.model = model
|
|
|
self.preprocessor = preprocessor
|
|
|
self.model_info = model_info
|
|
|
|
|
|
def calculate_lace_score(self, length_of_stay, acuity, comorbidity, emergency_visits):
|
|
|
"""Calculate LACE score for readmission risk"""
|
|
|
|
|
|
if length_of_stay >= 14:
|
|
|
length_points = 7
|
|
|
elif length_of_stay >= 7:
|
|
|
length_points = 5
|
|
|
elif length_of_stay >= 4:
|
|
|
length_points = 4
|
|
|
elif length_of_stay >= 3:
|
|
|
length_points = 3
|
|
|
elif length_of_stay == 2:
|
|
|
length_points = 2
|
|
|
elif length_of_stay == 1:
|
|
|
length_points = 1
|
|
|
else:
|
|
|
length_points = 0
|
|
|
|
|
|
|
|
|
acuity_points = 3 if acuity == "Emergency" else 0
|
|
|
|
|
|
|
|
|
if comorbidity >= 4:
|
|
|
comorbidity_points = 5
|
|
|
elif comorbidity >= 3:
|
|
|
comorbidity_points = 3
|
|
|
elif comorbidity >= 2:
|
|
|
comorbidity_points = 2
|
|
|
elif comorbidity == 1:
|
|
|
comorbidity_points = 1
|
|
|
else:
|
|
|
comorbidity_points = 0
|
|
|
|
|
|
|
|
|
if emergency_visits >= 4:
|
|
|
emergency_points = 4
|
|
|
elif emergency_visits >= 2:
|
|
|
emergency_points = 2
|
|
|
elif emergency_visits == 1:
|
|
|
emergency_points = 1
|
|
|
else:
|
|
|
emergency_points = 0
|
|
|
|
|
|
return length_points + acuity_points + comorbidity_points + emergency_points
|
|
|
|
|
|
def calculate_hospital_score(self, hemoglobin, discharge_sodium, length_of_stay,
|
|
|
procedure_count, admission_type, comorbidity_index):
|
|
|
"""Calculate HOSPITAL score"""
|
|
|
score = 0
|
|
|
|
|
|
|
|
|
if hemoglobin < 12:
|
|
|
score += 1
|
|
|
|
|
|
|
|
|
if procedure_count >= 3 and comorbidity_index >= 2:
|
|
|
score += 2
|
|
|
|
|
|
|
|
|
if discharge_sodium < 135:
|
|
|
score += 1
|
|
|
|
|
|
|
|
|
if procedure_count >= 1:
|
|
|
score += 1
|
|
|
|
|
|
|
|
|
if admission_type in ["Emergency", "Urgent"]:
|
|
|
score += 1
|
|
|
|
|
|
|
|
|
if length_of_stay >= 5:
|
|
|
score += 2
|
|
|
|
|
|
return score
|
|
|
|
|
|
def _prepare_features(self, age, time_in_hospital, n_lab_procedures, n_procedures,
|
|
|
n_medications, n_outpatient, n_inpatient, n_emergency,
|
|
|
medical_specialty, primary_diagnosis, admission_type,
|
|
|
discharge_disposition, glucose_test, a1c_test, diabetes_med,
|
|
|
change_diabetes_med, insulin, hemoglobin, sodium):
|
|
|
"""Prepare features for the ML model"""
|
|
|
features = []
|
|
|
|
|
|
|
|
|
features.extend([
|
|
|
age, time_in_hospital, n_lab_procedures, n_procedures,
|
|
|
n_medications, n_outpatient, n_inpatient, n_emergency,
|
|
|
hemoglobin, sodium
|
|
|
])
|
|
|
|
|
|
|
|
|
features.extend([
|
|
|
1 if age < 30 else 0,
|
|
|
1 if 30 <= age < 50 else 0,
|
|
|
1 if 50 <= age < 70 else 0,
|
|
|
1 if age >= 70 else 0,
|
|
|
])
|
|
|
|
|
|
|
|
|
specialty_map = {
|
|
|
"InternalMedicine": 0, "Cardiology": 1, "Surgery": 2,
|
|
|
"Family/GeneralPractice": 3, "Endocrinology": 4, "Orthopedics": 5,
|
|
|
"Psychiatry": 6, "Pediatrics": 7, "Emergency/Trauma": 8, "Other": 9
|
|
|
}
|
|
|
specialty_features = [0] * 10
|
|
|
if medical_specialty in specialty_map:
|
|
|
specialty_features[specialty_map[medical_specialty]] = 1
|
|
|
features.extend(specialty_features)
|
|
|
|
|
|
|
|
|
diagnosis_map = {
|
|
|
"Circulatory": 0, "Diabetes": 1, "Respiratory": 2, "Digestive": 3,
|
|
|
"Genitourinary": 4, "Injury": 5, "Musculoskeletal": 6,
|
|
|
"Neoplasms": 7, "Mental Disorders": 8, "Other": 8
|
|
|
}
|
|
|
diagnosis_features = [0] * 9
|
|
|
if primary_diagnosis in diagnosis_map:
|
|
|
diagnosis_features[diagnosis_map[primary_diagnosis]] = 1
|
|
|
features.extend(diagnosis_features)
|
|
|
|
|
|
|
|
|
features.extend([
|
|
|
1 if admission_type == "Emergency" else 0,
|
|
|
1 if admission_type == "Urgent" else 0,
|
|
|
1 if admission_type == "Elective" else 0,
|
|
|
1 if discharge_disposition == "Home" else 0,
|
|
|
1 if discharge_disposition == "Home Health Service" else 0,
|
|
|
1 if discharge_disposition == "Skilled Nursing Facility" else 0,
|
|
|
1 if diabetes_med == "Yes" else 0,
|
|
|
1 if glucose_test in [">200", ">300"] else 0,
|
|
|
1 if a1c_test in [">7", ">8", ">9"] else 0,
|
|
|
1 if insulin in ["Up", "Steady"] else 0
|
|
|
])
|
|
|
|
|
|
|
|
|
features.extend([
|
|
|
1 if hemoglobin < 12 else 0,
|
|
|
1 if sodium < 135 else 0,
|
|
|
1 if time_in_hospital >= 7 else 0,
|
|
|
1 if n_medications >= 15 else 0,
|
|
|
1 if n_emergency >= 2 else 0,
|
|
|
])
|
|
|
|
|
|
|
|
|
lace_score = self.calculate_lace_score(
|
|
|
time_in_hospital, admission_type,
|
|
|
min(3, (n_inpatient + n_emergency) // 2), n_emergency
|
|
|
)
|
|
|
hospital_score = self.calculate_hospital_score(
|
|
|
hemoglobin, sodium, time_in_hospital,
|
|
|
n_procedures, admission_type,
|
|
|
min(3, (n_inpatient + n_emergency) // 2)
|
|
|
)
|
|
|
features.extend([lace_score, hospital_score])
|
|
|
|
|
|
|
|
|
while len(features) < 62:
|
|
|
features.append(0)
|
|
|
features = features[:62]
|
|
|
|
|
|
return np.array(features).reshape(1, -1)
|
|
|
|
|
|
def predict_readmission(self, age, time_in_hospital, n_lab_procedures, n_procedures,
|
|
|
n_medications, n_outpatient, n_inpatient, n_emergency,
|
|
|
medical_specialty, primary_diagnosis, admission_type,
|
|
|
discharge_disposition, glucose_test, a1c_test, diabetes_med,
|
|
|
change_diabetes_med, insulin, hemoglobin, sodium):
|
|
|
"""Main prediction function"""
|
|
|
try:
|
|
|
|
|
|
lace_score = self.calculate_lace_score(
|
|
|
time_in_hospital, admission_type,
|
|
|
min(3, (n_inpatient + n_emergency) // 2), n_emergency
|
|
|
)
|
|
|
|
|
|
hospital_score = self.calculate_hospital_score(
|
|
|
hemoglobin, sodium, time_in_hospital,
|
|
|
n_procedures, admission_type,
|
|
|
min(3, (n_inpatient + n_emergency) // 2)
|
|
|
)
|
|
|
|
|
|
|
|
|
if self.model is not None:
|
|
|
try:
|
|
|
input_features = self._prepare_features(
|
|
|
age, time_in_hospital, n_lab_procedures, n_procedures,
|
|
|
n_medications, n_outpatient, n_inpatient, n_emergency,
|
|
|
medical_specialty, primary_diagnosis, admission_type,
|
|
|
discharge_disposition, glucose_test, a1c_test, diabetes_med,
|
|
|
change_diabetes_med, insulin, hemoglobin, sodium
|
|
|
)
|
|
|
|
|
|
readmission_probability = self.model.predict_proba(input_features)[0][1]
|
|
|
model_prediction = self.model.predict(input_features)[0]
|
|
|
prediction_source = "๐ค ML Model Prediction"
|
|
|
|
|
|
except Exception as e:
|
|
|
print(f"Model prediction failed: {e}, using clinical scoring")
|
|
|
readmission_probability = self._clinical_prediction(
|
|
|
age, time_in_hospital, n_medications, n_emergency,
|
|
|
n_inpatient, lace_score, hospital_score, hemoglobin, sodium
|
|
|
)
|
|
|
model_prediction = 1 if readmission_probability > 0.5 else 0
|
|
|
prediction_source = "๐ Clinical Scoring (Demo Mode)"
|
|
|
else:
|
|
|
|
|
|
readmission_probability = self._clinical_prediction(
|
|
|
age, time_in_hospital, n_medications, n_emergency,
|
|
|
n_inpatient, lace_score, hospital_score, hemoglobin, sodium
|
|
|
)
|
|
|
model_prediction = 1 if readmission_probability > 0.5 else 0
|
|
|
prediction_source = "๐ Clinical Scoring (Demo Mode)"
|
|
|
|
|
|
|
|
|
risk_factors = self._analyze_risk_factors(
|
|
|
age, time_in_hospital, n_medications, n_lab_procedures,
|
|
|
n_procedures, n_emergency, n_inpatient, diabetes_med,
|
|
|
glucose_test, a1c_test, insulin, hemoglobin, sodium,
|
|
|
medical_specialty, discharge_disposition
|
|
|
)
|
|
|
|
|
|
|
|
|
if readmission_probability >= 0.7:
|
|
|
risk_level = "๐ด VERY HIGH RISK"
|
|
|
risk_color = "#d32f2f"
|
|
|
recommendation = "Immediate intervention required. Consider discharge planning team, home health services, and close follow-up within 48-72 hours."
|
|
|
elif readmission_probability >= 0.5:
|
|
|
risk_level = "๐ HIGH RISK"
|
|
|
risk_color = "#f57c00"
|
|
|
recommendation = "Enhanced discharge planning recommended. Schedule follow-up within 7 days and consider transitional care services."
|
|
|
elif readmission_probability >= 0.3:
|
|
|
risk_level = "๐ก MODERATE RISK"
|
|
|
risk_color = "#ffa000"
|
|
|
recommendation = "Standard discharge planning with follow-up within 14 days. Monitor medication adherence."
|
|
|
else:
|
|
|
risk_level = "๐ข LOW RISK"
|
|
|
risk_color = "#388e3c"
|
|
|
recommendation = "Routine discharge planning. Standard follow-up care as clinically indicated."
|
|
|
|
|
|
|
|
|
return self._create_result_html(
|
|
|
risk_level, risk_color, readmission_probability, model_prediction,
|
|
|
lace_score, hospital_score, risk_factors, recommendation,
|
|
|
prediction_source, age, time_in_hospital, n_medications,
|
|
|
n_lab_procedures, n_procedures, n_emergency, n_inpatient,
|
|
|
n_outpatient, medical_specialty, primary_diagnosis,
|
|
|
hemoglobin, sodium
|
|
|
)
|
|
|
|
|
|
except Exception as e:
|
|
|
return self._create_error_output(f"โ Prediction Error: {str(e)}")
|
|
|
|
|
|
def _clinical_prediction(self, age, time_in_hospital, n_medications,
|
|
|
n_emergency, n_inpatient, lace_score, hospital_score,
|
|
|
hemoglobin, sodium):
|
|
|
"""Clinical scoring-based prediction for demo mode"""
|
|
|
base_risk = 0.2
|
|
|
|
|
|
|
|
|
if age >= 75:
|
|
|
base_risk += 0.15
|
|
|
elif age >= 65:
|
|
|
base_risk += 0.10
|
|
|
elif age >= 50:
|
|
|
base_risk += 0.05
|
|
|
|
|
|
|
|
|
if time_in_hospital >= 10:
|
|
|
base_risk += 0.20
|
|
|
elif time_in_hospital >= 7:
|
|
|
base_risk += 0.15
|
|
|
elif time_in_hospital >= 4:
|
|
|
base_risk += 0.10
|
|
|
|
|
|
|
|
|
if n_medications >= 20:
|
|
|
base_risk += 0.15
|
|
|
elif n_medications >= 15:
|
|
|
base_risk += 0.10
|
|
|
elif n_medications >= 10:
|
|
|
base_risk += 0.05
|
|
|
|
|
|
|
|
|
if n_emergency >= 3:
|
|
|
base_risk += 0.15
|
|
|
elif n_emergency >= 1:
|
|
|
base_risk += 0.10
|
|
|
|
|
|
if n_inpatient >= 2:
|
|
|
base_risk += 0.10
|
|
|
elif n_inpatient >= 1:
|
|
|
base_risk += 0.05
|
|
|
|
|
|
|
|
|
if hemoglobin < 10:
|
|
|
base_risk += 0.10
|
|
|
elif hemoglobin < 12:
|
|
|
base_risk += 0.05
|
|
|
|
|
|
if sodium < 130:
|
|
|
base_risk += 0.10
|
|
|
elif sodium < 135:
|
|
|
base_risk += 0.05
|
|
|
|
|
|
|
|
|
base_risk += lace_score * 0.02
|
|
|
base_risk += hospital_score * 0.03
|
|
|
|
|
|
return min(base_risk, 0.95)
|
|
|
|
|
|
def _analyze_risk_factors(self, age, time_in_hospital, n_medications,
|
|
|
n_lab_procedures, n_procedures, n_emergency,
|
|
|
n_inpatient, diabetes_med, glucose_test, a1c_test,
|
|
|
insulin, hemoglobin, sodium, medical_specialty,
|
|
|
discharge_disposition):
|
|
|
"""Analyze and return risk factors"""
|
|
|
risk_factors = []
|
|
|
|
|
|
if age >= 75:
|
|
|
risk_factors.append("Advanced age (75+)")
|
|
|
elif age >= 65:
|
|
|
risk_factors.append("Elderly (65-74)")
|
|
|
|
|
|
if time_in_hospital >= 10:
|
|
|
risk_factors.append("Extended hospitalization (10+ days)")
|
|
|
elif time_in_hospital >= 7:
|
|
|
risk_factors.append("Long hospitalization (7-9 days)")
|
|
|
|
|
|
if n_medications >= 20:
|
|
|
risk_factors.append("High medication burden (20+)")
|
|
|
elif n_medications >= 15:
|
|
|
risk_factors.append("Moderate medication burden (15-19)")
|
|
|
|
|
|
if n_emergency >= 3:
|
|
|
risk_factors.append("Frequent emergency visits (3+)")
|
|
|
elif n_emergency >= 1:
|
|
|
risk_factors.append("Recent emergency visits")
|
|
|
|
|
|
if n_inpatient >= 2:
|
|
|
risk_factors.append("Multiple previous admissions")
|
|
|
|
|
|
if diabetes_med == "Yes":
|
|
|
risk_factors.append("Diabetes medication")
|
|
|
if glucose_test in [">200", ">300"]:
|
|
|
risk_factors.append("Poor glucose control")
|
|
|
if a1c_test in [">8", ">9"]:
|
|
|
risk_factors.append("Poor diabetes control (HbA1c)")
|
|
|
|
|
|
if hemoglobin < 10:
|
|
|
risk_factors.append("Severe anemia")
|
|
|
elif hemoglobin < 12:
|
|
|
risk_factors.append("Mild anemia")
|
|
|
|
|
|
if sodium < 130:
|
|
|
risk_factors.append("Severe hyponatremia")
|
|
|
elif sodium < 135:
|
|
|
risk_factors.append("Mild hyponatremia")
|
|
|
|
|
|
if medical_specialty in ["Cardiology", "Surgery", "InternalMedicine"]:
|
|
|
risk_factors.append(f"High-risk specialty ({medical_specialty})")
|
|
|
|
|
|
if discharge_disposition in ["Home Health Service", "Skilled Nursing Facility"]:
|
|
|
risk_factors.append("Post-acute care needs")
|
|
|
|
|
|
return risk_factors
|
|
|
|
|
|
def _create_result_html(self, risk_level, risk_color, readmission_probability,
|
|
|
model_prediction, lace_score, hospital_score, risk_factors,
|
|
|
recommendation, prediction_source, age, time_in_hospital,
|
|
|
n_medications, n_lab_procedures, n_procedures, n_emergency,
|
|
|
n_inpatient, n_outpatient, medical_specialty, primary_diagnosis,
|
|
|
hemoglobin, sodium):
|
|
|
"""Create formatted HTML result"""
|
|
|
clinical_risk_score = len(risk_factors)
|
|
|
|
|
|
return f"""
|
|
|
<div style="padding: 25px; border-radius: 15px; background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;">
|
|
|
<div style="text-align: center; margin-bottom: 25px;">
|
|
|
<h1 style="color: #2c3e50; margin: 0; font-size: 28px;">๐ฅ Hospital Readmission Risk Assessment</h1>
|
|
|
<p style="color: #7f8c8d; margin: 5px 0; font-size: 16px;">{prediction_source}</p>
|
|
|
</div>
|
|
|
|
|
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;">
|
|
|
<div style="background: white; padding: 20px; border-radius: 12px; border-left: 5px solid {risk_color}; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
|
|
|
<h2 style="color: {risk_color}; margin-top: 0; font-size: 24px;">{risk_level}</h2>
|
|
|
<div style="margin: 15px 0;">
|
|
|
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
|
|
|
<span><strong>Readmission Probability:</strong></span>
|
|
|
<span style="color: {risk_color}; font-weight: bold;">{readmission_probability:.1%}</span>
|
|
|
</div>
|
|
|
<div style="background: #ecf0f1; height: 20px; border-radius: 10px; overflow: hidden;">
|
|
|
<div style="background: {risk_color}; height: 100%; width: {readmission_probability*100:.1f}%; border-radius: 10px;"></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<p><strong>Prediction:</strong> {'Readmission' if model_prediction == 1 else 'No Readmission'}</p>
|
|
|
<p><strong>Risk Factors:</strong> {clinical_risk_score}</p>
|
|
|
<p><strong>LACE Score:</strong> {lace_score}</p>
|
|
|
<p><strong>HOSPITAL Score:</strong> {hospital_score}</p>
|
|
|
</div>
|
|
|
|
|
|
<div style="background: white; padding: 20px; border-radius: 12px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
|
|
|
<h3 style="color: #2c3e50; margin-top: 0;">๐ Patient Summary</h3>
|
|
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px; font-size: 14px;">
|
|
|
<div><strong>Age:</strong> {age} years</div>
|
|
|
<div><strong>Length of Stay:</strong> {time_in_hospital} days</div>
|
|
|
<div><strong>Medications:</strong> {n_medications}</div>
|
|
|
<div><strong>Lab Procedures:</strong> {n_lab_procedures}</div>
|
|
|
<div><strong>Procedures:</strong> {n_procedures}</div>
|
|
|
<div><strong>Emergency Visits:</strong> {n_emergency}</div>
|
|
|
<div><strong>Previous Admissions:</strong> {n_inpatient}</div>
|
|
|
<div><strong>Outpatient Visits:</strong> {n_outpatient}</div>
|
|
|
<div><strong>Specialty:</strong> {medical_specialty}</div>
|
|
|
<div><strong>Primary Diagnosis:</strong> {primary_diagnosis}</div>
|
|
|
<div><strong>Hemoglobin:</strong> {hemoglobin} g/dL</div>
|
|
|
<div><strong>Sodium:</strong> {sodium} mEq/L</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div style="background: white; padding: 20px; border-radius: 12px; margin-bottom: 20px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
|
|
|
<h3 style="color: #2c3e50; margin-top: 0;">โ ๏ธ Identified Risk Factors</h3>
|
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 10px;">
|
|
|
{''.join([f'<div style="background: #fff3e0; padding: 10px; border-radius: 8px; border-left: 3px solid #ff9800;"><span style="color: #f57c00;">โข</span> {factor}</div>' for factor in risk_factors]) if risk_factors else '<p style="color: #27ae60; font-weight: bold;">โ
No major risk factors identified</p>'}
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div style="background: #e8f5e8; padding: 20px; border-radius: 12px; border-left: 5px solid #4caf50;">
|
|
|
<h3 style="color: #2e7d32; margin-top: 0;">๐ก Clinical Recommendations</h3>
|
|
|
<p style="margin: 0; color: #1b5e20; font-weight: 500;">{recommendation}</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
"""
|
|
|
|
|
|
def _create_error_output(self, error_message):
|
|
|
"""Create formatted error output"""
|
|
|
return f"""
|
|
|
<div style="padding: 20px; background: #ffebee; border-radius: 10px; border-left: 5px solid #f44336;">
|
|
|
<h3 style="color: #d32f2f; margin-top: 0;">โ Error</h3>
|
|
|
<p style="color: #c62828; margin: 0;">{error_message}</p>
|
|
|
<p style="color: #666; font-size: 12px; margin: 10px 0 0 0;">
|
|
|
Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
|
|
</p>
|
|
|
</div>
|
|
|
"""
|
|
|
|
|
|
|
|
|
predictor = HospitalReadmissionPredictor(model, preprocessor, model_info)
|
|
|
print("โ
Hospital Readmission Predictor initialized!")
|
|
|
|
|
|
|
|
|
def create_gradio_interface():
|
|
|
"""Create the Gradio interface for Hugging Face Spaces"""
|
|
|
|
|
|
with gr.Blocks(
|
|
|
title="๐ฅ Hospital Readmission Risk Predictor"
|
|
|
) as demo:
|
|
|
|
|
|
gr.Markdown("""
|
|
|
# ๐ฅ Hospital Readmission Risk Predictor
|
|
|
## AI-Powered Clinical Decision Support System
|
|
|
|
|
|
**Model Performance:** 74.71% Accuracy | AUC: 0.827 | RandomForest + SMOTEENN
|
|
|
|
|
|
This system uses machine learning to assess hospital readmission risk based on patient data and clinical indicators.
|
|
|
""")
|
|
|
|
|
|
with gr.Row():
|
|
|
|
|
|
with gr.Column(scale=1):
|
|
|
gr.Markdown("### ๐ค Patient Demographics")
|
|
|
age = gr.Slider(18, 100, value=65, step=1, label="Age (years)")
|
|
|
|
|
|
gr.Markdown("### ๐ฅ Hospital Stay")
|
|
|
time_in_hospital = gr.Slider(1, 30, value=4, step=1, label="Length of Stay (days)")
|
|
|
admission_type = gr.Dropdown(
|
|
|
["Elective", "Emergency", "Urgent", "Not Available"],
|
|
|
value="Emergency", label="Admission Type"
|
|
|
)
|
|
|
discharge_disposition = gr.Dropdown(
|
|
|
["Home", "Home Health Service", "Skilled Nursing Facility", "Other"],
|
|
|
value="Home", label="Discharge Disposition"
|
|
|
)
|
|
|
|
|
|
gr.Markdown("### โ๏ธ Medical Specialty")
|
|
|
medical_specialty = gr.Dropdown(
|
|
|
["InternalMedicine", "Cardiology", "Surgery", "Family/GeneralPractice",
|
|
|
"Endocrinology", "Orthopedics", "Psychiatry", "Pediatrics", "Other"],
|
|
|
value="InternalMedicine", label="Primary Medical Specialty"
|
|
|
)
|
|
|
primary_diagnosis = gr.Dropdown(
|
|
|
["Circulatory", "Diabetes", "Respiratory", "Digestive",
|
|
|
"Genitourinary", "Injury", "Musculoskeletal", "Neoplasms",
|
|
|
"Mental Disorders", "Other"],
|
|
|
value="Circulatory", label="Primary Diagnosis"
|
|
|
)
|
|
|
|
|
|
|
|
|
with gr.Column(scale=1):
|
|
|
gr.Markdown("### ๐ฌ Procedures & Tests")
|
|
|
n_lab_procedures = gr.Slider(0, 150, value=40, step=1, label="Lab Procedures")
|
|
|
n_procedures = gr.Slider(0, 15, value=2, step=1, label="Medical Procedures")
|
|
|
n_medications = gr.Slider(1, 50, value=12, step=1, label="Number of Medications")
|
|
|
|
|
|
gr.Markdown("### ๐
Healthcare History")
|
|
|
n_outpatient = gr.Slider(0, 40, value=2, step=1, label="Outpatient Visits (Past Year)")
|
|
|
n_inpatient = gr.Slider(0, 20, value=1, step=1, label="Inpatient Admissions (Past Year)")
|
|
|
n_emergency = gr.Slider(0, 25, value=1, step=1, label="Emergency Visits (Past Year)")
|
|
|
|
|
|
|
|
|
with gr.Column(scale=1):
|
|
|
gr.Markdown("### ๐ฉบ Laboratory Values")
|
|
|
hemoglobin = gr.Slider(5.0, 18.0, value=12.5, step=0.1, label="Hemoglobin (g/dL)")
|
|
|
sodium = gr.Slider(120, 150, value=140, step=1, label="Serum Sodium (mEq/L)")
|
|
|
|
|
|
gr.Markdown("### ๐ฏ Diabetes Management")
|
|
|
glucose_test = gr.Dropdown(
|
|
|
["None", "Norm", ">200", ">300", "Not Performed"],
|
|
|
value="None", label="Glucose Test"
|
|
|
)
|
|
|
a1c_test = gr.Dropdown(
|
|
|
["None", "Norm", ">7", ">8", ">9", "Not Performed"],
|
|
|
value="None", label="HbA1c Test"
|
|
|
)
|
|
|
diabetes_med = gr.Dropdown(["No", "Yes"], value="No", label="Diabetes Medication")
|
|
|
change_diabetes_med = gr.Dropdown(
|
|
|
["No", "Ch", "Up", "Down"], value="No", label="Change in Diabetes Med"
|
|
|
)
|
|
|
insulin = gr.Dropdown(
|
|
|
["No", "Down", "Steady", "Up"], value="No", label="Insulin Treatment"
|
|
|
)
|
|
|
|
|
|
|
|
|
predict_btn = gr.Button("๐ฎ Predict Readmission Risk", variant="primary", size="lg")
|
|
|
|
|
|
|
|
|
output = gr.HTML(label="Prediction Results")
|
|
|
|
|
|
|
|
|
gr.Markdown("""
|
|
|
---
|
|
|
**โ ๏ธ Disclaimer:** This tool is for clinical decision support only. Always consult healthcare professionals.
|
|
|
|
|
|
**๐ Model Info:** RandomForest + SMOTEENN | 74.71% Accuracy | 62 Features | LACE & HOSPITAL Scores
|
|
|
""")
|
|
|
|
|
|
|
|
|
predict_btn.click(
|
|
|
fn=predictor.predict_readmission,
|
|
|
inputs=[
|
|
|
age, time_in_hospital, n_lab_procedures, n_procedures,
|
|
|
n_medications, n_outpatient, n_inpatient, n_emergency,
|
|
|
medical_specialty, primary_diagnosis, admission_type,
|
|
|
discharge_disposition, glucose_test, a1c_test, diabetes_med,
|
|
|
change_diabetes_med, insulin, hemoglobin, sodium
|
|
|
],
|
|
|
outputs=output
|
|
|
)
|
|
|
|
|
|
return demo
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
demo = create_gradio_interface()
|
|
|
demo.launch()
|
|
|
|