CoolFace
Apppublic

Prach-404/Heart-Attack-Risk-Predictor

sourceHugging Faceapache-2.0updated 10mo agoView on Hugging Face
0likes
app.py562 linesDownload Raw Back to root
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