| import pickle |
|
|
| import numpy as np |
| import pandas as pd |
| import streamlit as st |
| import tensorflow as tf |
|
|
| st.set_page_config( |
| page_title="Heart Disease Risk Prediction", |
| page_icon="heart", |
| layout="wide", |
| ) |
|
|
| RISK_LABELS = ["Low", "Medium", "High"] |
|
|
| SEX_OPTIONS = {"Female": 0, "Male": 1} |
| CP_OPTIONS = { |
| "Typical angina": 0, |
| "Atypical angina": 1, |
| "Non-anginal pain": 2, |
| "Asymptomatic": 3, |
| } |
| FBS_OPTIONS = {"False": 0, "True": 1} |
| RESTECG_OPTIONS = { |
| "Normal": 0, |
| "ST-T wave abnormality": 1, |
| "Left ventricular hypertrophy": 2, |
| } |
| EXANG_OPTIONS = {"No": 0, "Yes": 1} |
| SLOPE_OPTIONS = {"Upsloping": 0, "Flat": 1, "Downsloping": 2} |
| CA_OPTIONS = {"0 vessels": 0, "1 vessel": 1, "2 vessels": 2, "3 vessels": 3, "4 vessels": 4} |
| THAL_OPTIONS = {"Fixed defect": 1, "Normal": 2, "Reversible defect": 3} |
|
|
|
|
| @st.cache_resource |
| def load_artifacts(): |
| model = tf.keras.models.load_model("heart_disease_ann.h5") |
| with open("preprocessor.pkl", "rb") as f: |
| preprocessor = pickle.load(f) |
| return model, preprocessor |
|
|
|
|
| def pick_code(label, options, help_text=None): |
| return options[st.selectbox(label, list(options.keys()), help=help_text)] |
|
|
|
|
| try: |
| model, preprocessor = load_artifacts() |
| except Exception as exc: |
| st.error(f"Could not load model files: {exc}") |
| st.stop() |
|
|
| st.title("Heart Disease Risk Prediction") |
| st.caption( |
| "Enter patient details, run prediction, and review risk probabilities. " |
| "This tool supports screening and is not a medical diagnosis." |
| ) |
|
|
| tab_predict, tab_guide = st.tabs(["Prediction", "Input Guide"]) |
|
|
| with tab_predict: |
| with st.form(key="heart_form"): |
| col_a, col_b = st.columns(2) |
|
|
| with col_a: |
| st.subheader("Patient Profile") |
| age = st.slider("Age", min_value=1, max_value=120, value=30) |
| sex = pick_code("Sex", SEX_OPTIONS, "Male = 1, Female = 0") |
| cp = pick_code("Chest Pain Type", CP_OPTIONS) |
| exang = pick_code("Exercise Induced Angina", EXANG_OPTIONS, "Yes = 1, No = 0") |
| slope = pick_code("Slope of ST Segment", SLOPE_OPTIONS) |
| thal = pick_code("Thalassemia", THAL_OPTIONS) |
|
|
| with col_b: |
| st.subheader("Clinical Measurements") |
| trestbps = st.slider("Resting Blood Pressure (mm Hg)", min_value=80, max_value=220, value=120) |
| chol = st.slider("Cholesterol (mg/dl)", min_value=100, max_value=600, value=200) |
| fbs = pick_code("Fasting Blood Sugar > 120 mg/dl?", FBS_OPTIONS, "True = 1, False = 0") |
| restecg = pick_code("Resting ECG Result", RESTECG_OPTIONS) |
| thalach = st.slider("Max Heart Rate Achieved", min_value=60, max_value=220, value=150) |
| oldpeak = st.number_input("ST Depression (oldpeak)", min_value=0.0, max_value=10.0, value=1.0, step=0.1) |
| ca = pick_code("Major Vessels Colored by Fluoroscopy", CA_OPTIONS) |
|
|
| submit_button = st.form_submit_button("Predict Risk", use_container_width=True) |
|
|
| if submit_button: |
| input_df = pd.DataFrame( |
| [ |
| { |
| "age": age, |
| "sex": sex, |
| "cp": cp, |
| "trestbps": trestbps, |
| "chol": chol, |
| "fbs": fbs, |
| "restecg": restecg, |
| "thalach": thalach, |
| "exang": exang, |
| "oldpeak": oldpeak, |
| "slope": slope, |
| "ca": ca, |
| "thal": thal, |
| } |
| ] |
| ) |
|
|
| input_processed = preprocessor.transform(input_df) |
| pred_probs = model.predict(input_processed, verbose=0)[0] |
| pred_class = RISK_LABELS[int(np.argmax(pred_probs))] |
|
|
| if pred_class == "Low": |
| st.success(f"Predicted Risk Level: {pred_class}") |
| elif pred_class == "Medium": |
| st.warning(f"Predicted Risk Level: {pred_class}") |
| else: |
| st.error(f"Predicted Risk Level: {pred_class}") |
|
|
| st.subheader("Probability Scores") |
| metric_cols = st.columns(3) |
| for idx, label in enumerate(RISK_LABELS): |
| metric_cols[idx].metric(label, f"{pred_probs[idx] * 100:.2f}%") |
|
|
| prob_df = pd.DataFrame( |
| {"Risk": RISK_LABELS, "Probability": (pred_probs * 100).round(2)} |
| ).set_index("Risk") |
| st.bar_chart(prob_df) |
|
|
| with st.expander("Show Submitted Values"): |
| st.dataframe(input_df, hide_index=True, use_container_width=True) |
|
|
| with tab_guide: |
| st.subheader("Category Encoding Reference") |
| st.write("Sex: Female = 0, Male = 1") |
| st.write("Chest Pain Type: Typical angina = 0, Atypical angina = 1, Non-anginal pain = 2, Asymptomatic = 3") |
| st.write("Fasting Blood Sugar > 120 mg/dl: False = 0, True = 1") |
| st.write( |
| "Resting ECG Result: Normal = 0, ST-T wave abnormality = 1, " |
| "Left ventricular hypertrophy = 2" |
| ) |
| st.write("Exercise Induced Angina: No = 0, Yes = 1") |
| st.write("Slope of ST Segment: Upsloping = 0, Flat = 1, Downsloping = 2") |
| st.write("Major Vessels Colored by Fluoroscopy: Number of vessels from 0 to 4") |
| st.write("Thalassemia: Fixed defect = 1, Normal = 2, Reversible defect = 3") |
|
|