Prach-404/Heart-Attack-Risk-Predictor
0
1import streamlit as st2import pandas as pd3import numpy as np4import pickle5import plotly.graph_objects as go6from sklearn.preprocessing import LabelEncoder, StandardScaler7 8# ============================================9# PAGE CONFIGURATION10# ============================================11st.set_page_config(12 page_title="Heart Risk Predictor",13 page_icon="๐",14 layout="wide",15 initial_sidebar_state="expanded"16)17 18# ============================================19# CUSTOM CSS - SOFT PINK THEME20# ============================================21st.markdown("""22<style>23 /* Main background */24 .stApp {25 background-color: #ffe6f2;26 }27 28 /* Sidebar */29 [data-testid="stSidebar"] {30 background-color: #ffccdd;31 }32 33 /* Headers */34 h1, h2, h3 {35 color: #660033 !important;36 font-weight: bold !important;37 }38 39 /* Text */40 p, label, .stMarkdown {41 color: #660033 !important;42 }43 44 /* Input boxes */45 .stTextInput input, .stNumberInput input, .stSelectbox select {46 background-color: #fff0f5 !important;47 color: #660033 !important;48 border: 2px solid #ff99cc !important;49 border-radius: 10px !important;50 }51 52 /* Buttons */53 .stButton button {54 background-color: #ff99cc !important;55 color: white !important;56 font-weight: bold !important;57 border-radius: 10px !important;58 border: none !important;59 padding: 10px 24px !important;60 transition: all 0.3s !important;61 }62 63 .stButton button:hover {64 background-color: #ff66b3 !important;65 transform: scale(1.05) !important;66 }67 68 /* Cards/Containers */69 .css-1r6slb0, .css-12oz5g7 {70 background-color: #fff0f5 !important;71 border-radius: 15px !important;72 padding: 20px !important;73 border: 2px solid #ff99cc !important;74 }75 76 /* Success/Info boxes */77 .stSuccess, .stInfo {78 background-color: #ffccdd !important;79 color: #660033 !important;80 border-radius: 10px !important;81 }82 83 /* Metrics */84 [data-testid="stMetricValue"] {85 color: #cc0066 !important;86 font-size: 2em !important;87 }88 89 /* Divider */90 hr {91 border-color: #ff99cc !important;92 }93</style>94""", unsafe_allow_html=True)95 96# ============================================97# LOAD MODEL AND PREPROCESSING OBJECTS98# ============================================99@st.cache_resource100def load_model_objects():101 """Load the trained model and preprocessing objects from .pkl files"""102 try:103 # Load all saved .pkl files104 with open("lr_model.pkl", "rb") as f:105 model = pickle.load(f)106 107 with open("label_encoders.pkl", "rb") as f:108 label_encoders = pickle.load(f)109 110 with open("scaler.pkl", "rb") as f:111 scaler = pickle.load(f)112 113 with open("target_encoder.pkl", "rb") as f:114 target_encoder = pickle.load(f)115 116 with open("feature_columns.pkl", "rb") as f:117 feature_columns = pickle.load(f)118 119 with open("categorical_cols.pkl", "rb") as f:120 categorical_cols = pickle.load(f)121 122 with open("numerical_cols.pkl", "rb") as f:123 numerical_cols = pickle.load(f)124 125 # Load original dataset if needed126 df_original = pd.read_csv("heart_dataset_with_diet_quality.csv")127 return {128 'model': model,129 'label_encoders': label_encoders,130 'scaler': scaler,131 'target_encoder': target_encoder,132 'feature_columns': feature_columns,133 'categorical_cols': categorical_cols,134 'numerical_cols': numerical_cols,135 'df_original': df_original136 }137 138 except Exception as e:139 st.error(f"Error loading model objects: {str(e)}")140 return None141 142# ============================================143# HELPER FUNCTIONS144# ============================================145def calculate_bmi(weight_kg, height_cm):146 """Calculate BMI from weight (kg) and height (cm)"""147 height_m = height_cm / 100148 bmi = weight_kg / (height_m ** 2)149 return round(bmi, 2)150 151def feet_to_cm(feet, inches=0):152 """Convert feet and inches to centimeters"""153 total_inches = (feet * 12) + inches154 cm = total_inches * 2.54155 return round(cm, 2)156 157# ============================================158# MAIN APP159# ============================================160def main():161 # Header162 st.markdown("<h1 style='text-align: center;'>๐ Heart Disease Risk Predictor</h1>", unsafe_allow_html=True)163 st.markdown("<p style='text-align: center; font-size: 1.2em;'>Enter patient information to predict cardiovascular risk level</p>", unsafe_allow_html=True)164 st.markdown("---")165 166 # Load model objects167 model_objects = load_model_objects()168 169 if model_objects is None:170 st.error("Failed to load model. Please ensure the model is trained in the notebook.")171 return172 173 model = model_objects['model']174 label_encoders = model_objects['label_encoders']175 scaler = model_objects['scaler']176 target_encoder = model_objects['target_encoder']177 feature_columns = model_objects['feature_columns']178 categorical_cols = model_objects['categorical_cols']179 numerical_cols = model_objects['numerical_cols']180 df_original = model_objects['df_original']181 182 # Sidebar info183 with st.sidebar:184 st.markdown("### ๐ Model Information")185 st.info(f"""186 **Model:** Logistic Regression 187 **Features:** {len(feature_columns)} 188 **Risk Classes:** {len(target_encoder.classes_)}189 190 """)191 192 st.markdown("### ๐ฏ Risk Classes")193 for cls in target_encoder.classes_:194 st.markdown(f"โข {cls}")195 196 # Create input form197 st.markdown("## ๐ Patient Information")198 199 # Create columns for better layout200 col1, col2 = st.columns(2)201 202 user_input = {}203 204 # Special fields for height, weight, BMI205 height_cm = None206 weight_kg = None207 calculated_bmi = None208 209 with col1:210 st.markdown("### Personal & Medical Factors")211 user_name = st.text_input("Name", placeholder="Enter name")212 213 for i, col in enumerate(feature_columns[:len(feature_columns)//2]):214 # Special handling for Age215 if col.lower() == 'age':216 user_input[col] = st.number_input(217 f"{col.replace('_', ' ').title()}",218 min_value=0,219 max_value=120,220 value=int(df_original[col].mean()),221 step=1,222 key=f"input_{col}"223 )224 # Special handling for Alcohol Consumption225 elif 'alcohol' in col.lower():226 user_input[col] = st.number_input(227 f"{col.replace('_', ' ').title()} (0-10 scale)",228 min_value=0,229 max_value=10,230 value=0,231 step=1,232 key=f"input_{col}"233 )234 # Special handling for Smoking235 elif 'smoking' in col.lower() or 'smoke' in col.lower():236 user_input[col] = st.number_input(237 f"{col.replace('_', ' ').title()} (0-10 scale)",238 min_value=0,239 max_value=10,240 value=0,241 step=1,242 key=f"input_{col}"243 )244 # Special handling for Exercise245 elif 'exercise' in col.lower():246 user_input[col] = st.number_input(247 f"{col.replace('_', ' ').title()} (0-10 scale)",248 min_value=0,249 max_value=10,250 value=5,251 step=1,252 key=f"input_{col}"253 )254 # Special handling for Height255 elif col.lower() == 'height' or 'height' in col.lower():256 st.markdown(f"**{col.replace('_', ' ').title()}**")257 height_unit = st.radio("Height Unit", ["Centimeters", "Feet"], key=f"unit_{col}", horizontal=True)258 259 if height_unit == "Centimeters":260 height_cm = st.number_input(261 "Height (cm)",262 min_value=50.0,263 max_value=250.0,264 value=170.0,265 step=0.1,266 key=f"input_{col}_cm"267 )268 else:269 col_ft, col_in = st.columns(2)270 with col_ft:271 feet = st.number_input("Feet", min_value=1, max_value=8, value=5, step=1, key=f"input_{col}_ft")272 with col_in:273 inches = st.number_input("Inches", min_value=0, max_value=11, value=7, step=1, key=f"input_{col}_in")274 height_cm = feet_to_cm(feet, inches)275 st.info(f"Height: {height_cm} cm")276 277 user_input[col] = height_cm278 279 # Special handling for Weight280 elif col.lower() == 'weight' or 'weight' in col.lower():281 weight_kg = st.number_input(282 f"{col.replace('_', ' ').title()} (kg)",283 min_value=20.0,284 max_value=300.0,285 value=70.0,286 step=0.1,287 key=f"input_{col}"288 )289 user_input[col] = weight_kg290 291 # Special handling for BMI292 elif col.lower() == 'bmi' or col == 'BMI':293 # Calculate BMI if height and weight are available294 if height_cm is not None and weight_kg is not None:295 calculated_bmi = calculate_bmi(weight_kg, height_cm)296 st.markdown(f"**{col.replace('_', ' ').title()}** (Auto-calculated)")297 st.info(f"BMI: {calculated_bmi}")298 user_input[col] = calculated_bmi299 else:300 # Fallback if height/weight not yet entered301 user_input[col] = st.number_input(302 f"{col.replace('_', ' ').title()} (will be auto-calculated)",303 min_value=10.0,304 max_value=60.0,305 value=float(df_original[col].mean()) if col in df_original.columns else 25.0,306 disabled=True,307 key=f"input_{col}"308 )309 310 elif col in categorical_cols:311 # Get unique values from original dataframe312 unique_values = sorted(df_original[col].dropna().unique().tolist())313 user_input[col] = st.selectbox(314 f"{col.replace('_', ' ').title()}",315 options=unique_values,316 key=f"input_{col}"317 )318 elif col in numerical_cols:319 # Get min, max, mean for default values320 min_val = float(df_original[col].min())321 max_val = float(df_original[col].max())322 mean_val = float(df_original[col].mean())323 324 user_input[col] = st.number_input(325 f"{col.replace('_', ' ').title()}",326 min_value=min_val,327 max_value=max_val,328 value=mean_val,329 key=f"input_{col}"330 )331 332 with col2:333 st.markdown("### Additional Factors")334 for i, col in enumerate(feature_columns[len(feature_columns)//2:]):335 # Special handling for Age336 if col.lower() == 'age':337 user_input[col] = st.number_input(338 f"{col.replace('_', ' ').title()}",339 min_value=0,340 max_value=120,341 value=int(df_original[col].mean()),342 step=1,343 key=f"input_{col}"344 )345 # Special handling for Alcohol Consumption346 elif 'alcohol' in col.lower():347 user_input[col] = st.number_input(348 f"{col.replace('_', ' ').title()} (0-10 scale)",349 min_value=0,350 max_value=10,351 value=0,352 step=1,353 key=f"input_{col}"354 )355 # Special handling for Smoking356 elif 'smoking' in col.lower() or 'smoke' in col.lower():357 user_input[col] = st.number_input(358 f"{col.replace('_', ' ').title()} (0-10 scale)",359 min_value=0,360 max_value=10,361 value=0,362 step=1,363 key=f"input_{col}"364 )365 # Special handling for Exercise366 elif 'exercise' in col.lower():367 user_input[col] = st.number_input(368 f"{col.replace('_', ' ').title()} (0-10 scale)",369 min_value=0,370 max_value=10,371 value=5,372 step=1,373 key=f"input_{col}"374 )375 # Special handling for Height376 elif col.lower() == 'height' or 'height' in col.lower():377 st.markdown(f"**{col.replace('_', ' ').title()}**")378 height_unit = st.radio("Height Unit", ["Centimeters", "Feet"], key=f"unit_{col}", horizontal=True)379 380 if height_unit == "Centimeters":381 height_cm = st.number_input(382 "Height (cm)",383 min_value=50.0,384 max_value=250.0,385 value=170.0,386 step=0.1,387 key=f"input_{col}_cm"388 )389 else:390 col_ft, col_in = st.columns(2)391 with col_ft:392 feet = st.number_input("Feet", min_value=1, max_value=8, value=5, step=1, key=f"input_{col}_ft")393 with col_in:394 inches = st.number_input("Inches", min_value=0, max_value=11, value=7, step=1, key=f"input_{col}_in")395 height_cm = feet_to_cm(feet, inches)396 st.info(f"Height: {height_cm} cm")397 398 user_input[col] = height_cm399 400 # Special handling for Weight401 elif col.lower() == 'weight' or 'weight' in col.lower():402 weight_kg = st.number_input(403 f"{col.replace('_', ' ').title()} (kg)",404 min_value=20.0,405 max_value=300.0,406 value=70.0,407 step=0.1,408 key=f"input_{col}"409 )410 user_input[col] = weight_kg411 412 # Special handling for BMI413 elif col.lower() == 'bmi' or col == 'BMI':414 # Calculate BMI if height and weight are available415 if height_cm is not None and weight_kg is not None:416 calculated_bmi = calculate_bmi(weight_kg, height_cm)417 st.markdown(f"**{col.replace('_', ' ').title()}** (Auto-calculated)")418 st.info(f"BMI: {calculated_bmi}")419 user_input[col] = calculated_bmi420 else:421 # Fallback if height/weight not yet entered422 user_input[col] = st.number_input(423 f"{col.replace('_', ' ').title()} (will be auto-calculated)",424 min_value=10.0,425 max_value=60.0,426 value=float(df_original[col].mean()) if col in df_original.columns else 25.0,427 disabled=True,428 key=f"input_{col}"429 )430 431 elif col in categorical_cols:432 unique_values = sorted(df_original[col].dropna().unique().tolist())433 user_input[col] = st.selectbox(434 f"{col.replace('_', ' ').title()}",435 options=unique_values,436 key=f"input_{col}"437 )438 elif col in numerical_cols:439 min_val = float(df_original[col].min())440 max_val = float(df_original[col].max())441 mean_val = float(df_original[col].mean())442 443 user_input[col] = st.number_input(444 f"{col.replace('_', ' ').title()}",445 min_value=min_val,446 max_value=max_val,447 value=mean_val,448 key=f"input_{col}"449 )450 451 st.markdown("---")452 # Prediction button453 col_btn1, col_btn2, col_btn3 = st.columns([1, 1, 1])454 with col_btn2:455 predict_button = st.button("๐ฎ Predict Risk Level", use_container_width=True)456 457 if predict_button:458 try:459 # Prepare input data460 input_df = pd.DataFrame([user_input])461 462 # Encode categorical features463 for col in categorical_cols:464 if col in label_encoders:465 input_df[col] = label_encoders[col].transform(input_df[col].astype(str))466 467 # Scale numerical features468 input_df[numerical_cols] = scaler.transform(input_df[numerical_cols])469 470 # Ensure correct feature order471 input_df = input_df[feature_columns]472 473 # Make prediction474 prediction = model.predict(input_df)[0]475 probabilities = model.predict_proba(input_df)[0]476 477 predicted_class = target_encoder.inverse_transform([prediction])[0]478 479 # Display results480 st.markdown("---")481 st.markdown("## ๐ฏ Prediction Results")482 483 # Main prediction484 st.markdown(f"<h2 style='text-align: center; color: #cc0066;'>Predicted Risk Level: {predicted_class}</h2>", unsafe_allow_html=True)485 486 # Probability distribution487 st.markdown("### ๐ Risk Probability Distribution")488 489 # Create probability chart490 classes = target_encoder.classes_491 prob_df = pd.DataFrame({492 'Risk Level': classes,493 'Probability': probabilities * 100494 })495 496 # Sort by probability497 prob_df = prob_df.sort_values('Probability', ascending=True)498 499 # Create horizontal bar chart500 fig = go.Figure(go.Bar(501 x=prob_df['Probability'],502 y=prob_df['Risk Level'],503 orientation='h',504 marker=dict(505 color=prob_df['Probability'],506 colorscale=[[0, '#ffccdd'], [0.5, '#ff99cc'], [1, '#cc0066']],507 showscale=False508 ),509 text=[f'{p:.1f}%' for p in prob_df['Probability']],510 textposition='outside'511 ))512 513 fig.update_layout(514 title='Risk Level Probabilities',515 xaxis_title='Probability (%)',516 yaxis_title='Risk Level',517 plot_bgcolor='#ffe6f2',518 paper_bgcolor='#ffe6f2',519 font=dict(color='#660033', size=12),520 height=400521 )522 523 st.plotly_chart(fig, use_container_width=True)524 525 # Detailed probabilities526 st.markdown("### ๐ Detailed Probabilities")527 cols = st.columns(len(classes))528 for i, (cls, prob) in enumerate(zip(classes, probabilities)):529 with cols[i]:530 st.metric(531 label=cls,532 value=f"{prob*100:.2f}%"533 )534 535 # Confidence indicator536 max_prob = max(probabilities)537 if max_prob > 0.7:538 confidence = "High"539 color = "#00cc66"540 elif max_prob > 0.5:541 confidence = "Medium"542 color = "#ff9900"543 else:544 confidence = "Low"545 color = "#cc0066"546 547 st.markdown(f"<p style='text-align: center; font-size: 1.2em;'>Prediction Confidence: <strong style='color: {color};'>{confidence}</strong> ({max_prob*100:.1f}%)</p>", unsafe_allow_html=True)548 st.sidebar.markdown("""549 **โ ๏ธ Disclaimer:** 550 This predictor provides **informational results only**. 551 It is **not a medical diagnosis**. 552 Consult a **healthcare professional** for medical advice.553 """)554 555 except Exception as e:556 st.error(f"Error making prediction: {str(e)}")557 st.exception(e)558 559 560if __name__ == "__main__":561 main()562 