medical-analysis / app_bmi.py
drkareemkamal's picture
Create app_bmi.py
85bb5aa verified
raw
history blame
13.5 kB
# streamlit_health_dashboard.py
import msoffcrypto
import streamlit as st
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from io import BytesIO
import os
# Create a session state variable for login status
# Initialize session state variables
if "authenticated" not in st.session_state:
st.session_state.authenticated = False
# Function to log out
def logout():
st.session_state.authenticated = False
st.rerun()
# If not authenticated, show login form
if not st.session_state.authenticated:
with st.form("login_form"):
password_input = st.text_input("Enter password to access the dashboard:", type="password")
submitted = st.form_submit_button("Login")
if submitted:
if password_input == os.environ.get("password"):
st.session_state.authenticated = True
st.success("Login successful!")
st.rerun()
else:
st.error("Incorrect password. Please try again.")
st.stop()
else:
# Show logout button at the top after login
st.sidebar.button("πŸšͺ Logout", on_click=logout)
local_excel_path = "all_BMI.xlsx"
if os.path.exists(local_excel_path):
#st.success(f"File found locally")
try :
with open(local_excel_path, "rb") as f:
office_file = msoffcrypto.OfficeFile(f)
office_file.load_key(password=os.environ.get('password')) # Provide the password
decrypted = BytesIO()
office_file.decrypt(decrypted)
df = pd.read_excel(decrypted)
df.columns = [str(col).strip() for col in df.columns]
except :
df = pd.read_excel(local_excel_path)
df.columns = [str(col).strip() for col in df.columns]
# df = pd.read_excel(local_excel_path)
# Load dataset
else :
uploaded_file = st.file_uploader("Upload your Excel file", type=["xlsx"])
if uploaded_file is not None:
try :
# with open(uploaded_file, "rb") as f:
office_file = msoffcrypto.OfficeFile(uploaded_file)
office_file.load_key(password="Kemo3689") # Provide the password
decrypted = BytesIO()
office_file.decrypt(decrypted)
df = pd.read_excel(decrypted)
df.columns = [str(col).strip() for col in df.columns]
except :
df = pd.read_excel(uploaded_file)
df.columns = [str(col).strip() for col in df.columns]
# Data Cleaning
numeric_cols = ['Cholesterol', 'Triglycerides', 'HbA1c', 'UricAcid', 'Creatinine', 'SGOT(AST)', 'SGPT(ALT)']
for col in numeric_cols:
df[col] = pd.to_numeric(df[col], errors='coerce')
# Sidebar: Year Filter
if 'year' in df.columns:
available_years = sorted(df['year'].dropna().unique())
selected_year = st.sidebar.selectbox("Select Year", available_years)
df = df[df['year'] == selected_year]
dashboard_year = selected_year
else:
dashboard_year = 2024 # default if no year column exists
# Sidebar: Page selection
page = st.sidebar.selectbox("Select Page", ["Main Overview", "Detailed Analysis", "Advanced Analytics"])
# Sidebar: Thresholds
st.sidebar.header("Threshold Settings 🎯")
uric_acid_thresh = st.sidebar.slider("Uric Acid Threshold", 5.0, 10.0, 7.0)
creatinine_thresh = st.sidebar.slider("Creatinine Threshold", 0.8, 2.0, 1.2)
sgot_thresh = st.sidebar.slider("SGOT(AST) Threshold", 30, 100, 50)
sgpt_thresh = st.sidebar.slider("SGPT(ALT) Threshold", 30, 100, 50)
# Sidebar: Filters
st.sidebar.header("Apply Filters πŸ”Ž")
show_high_cholesterol = st.sidebar.checkbox("Show High Cholesterol Only (>220)")
show_high_tgs = st.sidebar.checkbox("Show High TGs Only (>150)")
show_high_hba1c = st.sidebar.checkbox("Show High HbA1c Only (>6.4)")
show_pre_diabetes = st.sidebar.checkbox("Show Pre-Diabetes (5.7-6.4)")
# Sidebar: Theme
theme = st.sidebar.radio("Choose Theme πŸŒ—", ["Light", "Dark"])
if theme == "Dark":
st.markdown("""
<style>
body { background-color: #0e1117; color: white; transition: all 0.5s ease; }
</style>
""", unsafe_allow_html=True)
else:
st.markdown("""
<style>
body { background-color: white; color: black; transition: all 0.5s ease; }
</style>
""", unsafe_allow_html=True)
# Apply Filters
filtered_df = df.copy()
if show_high_cholesterol:
filtered_df = filtered_df[filtered_df['Cholesterol'] > 220]
if show_high_tgs:
filtered_df = filtered_df[filtered_df['Triglycerides'] > 150]
if show_high_hba1c:
filtered_df = filtered_df[filtered_df['HbA1c'] > 6.4]
if show_pre_diabetes:
filtered_df = filtered_df[(filtered_df['HbA1c'] >= 5.7) & (filtered_df['HbA1c'] <= 6.4)]
# Drop NA values for BMI, Wg, Hg
filtered_df = filtered_df.dropna(subset=['BMI', 'Wg', 'Hg'])
# Ensure BMI is numeric
filtered_df['BMI'] = pd.to_numeric(filtered_df['BMI'], errors='coerce')
# Add BMI Category Column
def classify_bmi(bmi):
if pd.isna(bmi):
return "Unknown"
elif bmi < 18.5:
return "Underweight"
elif 18.5 <= bmi < 25:
return "Normal"
elif 25 <= bmi < 30:
return "Overweight"
else:
return "Obese"
filtered_df['BMI Category'] = filtered_df['BMI'].apply(classify_bmi)
### ----------------- Main Overview Page -------------------
if page == "Main Overview":
st.title(f"Annual Health Checkup Dashboard (Data Year: {dashboard_year})")
st.header("Key Performance Indicators πŸ“ˆ")
# Calculate safe KPIs (dropna before mean)
high_uric_acid = (filtered_df['UricAcid'].dropna() > uric_acid_thresh).mean() * 100
high_creatinine = (filtered_df['Creatinine'].dropna() > creatinine_thresh).mean() * 100
elevated_sgot = (filtered_df['SGOT(AST)'].dropna() > sgot_thresh).mean() * 100
elevated_sgpt = (filtered_df['SGPT(ALT)'].dropna() > sgpt_thresh).mean() * 100
high_cholesterol = (filtered_df['Cholesterol'].dropna() > 220).mean() * 100
high_tgs = (filtered_df['Triglycerides'].dropna() > 150).mean() * 100
high_hba1c = (filtered_df['HbA1c'].dropna() > 6.2).mean() * 100
col1, col2, col3 = st.columns(3)
col1.metric("High Uric Acid %", f"{high_uric_acid:.1f}%")
col2.metric("High Creatinine %", f"{high_creatinine:.1f}%")
col3.metric("Elevated SGOT/SGPT %", f"{(elevated_sgot + elevated_sgpt)/2:.1f}%")
col4, col5 = st.columns(2)
col4.metric("High Cholesterol %", f"{high_cholesterol:.1f}%")
col5.metric("High Triglycerides %", f"{high_tgs:.1f}%")
st.header("Distribution Charts πŸ“Š")
fig = px.histogram(filtered_df, x='Cholesterol', nbins=30, title='Cholesterol Distribution')
st.plotly_chart(fig)
fig = px.histogram(filtered_df, x='Triglycerides', nbins=30, title='Triglycerides Distribution')
st.plotly_chart(fig)
fig = px.histogram(filtered_df, x='HbA1c', nbins=30, title='HbA1c Distribution')
st.plotly_chart(fig)
st.header("Pie Charts for Elevated Metrics πŸ₯§")
metrics = {
'Cholesterol >220': filtered_df['Cholesterol'] > 220,
'Triglycerides >150': filtered_df['Triglycerides'] > 150,
'HbA1c >6.2': filtered_df['HbA1c'] > 6.4,
'Pre-Diabetic' : filtered_df['HbA1c'].between(5.7, 6.4),
'Uric Acid': filtered_df['UricAcid'] > uric_acid_thresh,
'Creatinine': filtered_df['Creatinine'] > creatinine_thresh,
'SGOT(AST)': filtered_df['SGOT(AST)'] > sgot_thresh,
'SGPT(ALT)': filtered_df['SGPT(ALT)'] > sgpt_thresh
}
for metric_name, condition in metrics.items():
fig = px.pie(names=["Above Threshold", "Normal"], values=[condition.sum(), (~condition).sum()], title=metric_name)
st.plotly_chart(fig)
st.subheader("BMI, Weight, and Height Distribution (Histograms)")
st.plotly_chart(px.histogram(filtered_df, x='BMI', nbins=30, title='BMI Distribution'))
st.plotly_chart(px.histogram(filtered_df, x='Wg', nbins=30, title='Weight (Wg) Distribution'))
st.plotly_chart(px.histogram(filtered_df, x='Hg', nbins=30, title='Height (Hg) Distribution'))
# KDE Density Plots
st.subheader("BMI, Weight, and Height Distribution (KDE)")
fig, ax = plt.subplots(1, 3, figsize=(18, 5))
sns.kdeplot(filtered_df['BMI'].dropna(), fill=True, ax=ax[0])
ax[0].set_title('BMI Density')
sns.kdeplot(filtered_df['Wg'].dropna(), fill=True, ax=ax[1])
ax[1].set_title('Weight Density (Wg)')
sns.kdeplot(filtered_df['Hg'].dropna(), fill=True, ax=ax[2])
ax[2].set_title('Height Density (Hg)')
st.pyplot(fig)
# BMI Category Charts
st.subheader("BMI Category Distribution πŸ§β€β™‚οΈ")
bmi_counts = filtered_df['BMI Category'].value_counts().reset_index()
bmi_counts.columns = ['BMI Category', 'Count']
st.plotly_chart(px.pie(bmi_counts, names='BMI Category', values='Count', title="Proportion of Individuals by BMI Category"))
st.plotly_chart(px.bar(bmi_counts, x='BMI Category', y='Count', title="Individuals per BMI Category", color='BMI Category'))
# Color-coded Alerts
st.subheader("BMI Category Alerts 🚦")
bmi_categories = {
"Underweight": {"color": "#007bff", "emoji": "πŸ§β€β™‚οΈ", "msg": "Underweight individuals detected"},
"Normal": {"color": "#28a745", "emoji": "βœ…", "msg": "Normal BMI range"},
"Overweight": {"color": "#fd7e14", "emoji": "⚠️", "msg": "Overweight individuals present"},
"Obese": {"color": "#dc3545", "emoji": "🚨", "msg": "Obese individuals found - high risk"}
}
for category, info in bmi_categories.items():
count = (filtered_df['BMI Category'] == category).sum()
if count > 0:
st.markdown(
f"""<div style=\"background-color:{info['color']}; padding:10px; border-radius:8px; color:white; margin-bottom:10px\">
<strong>{info['emoji']} {category}:</strong> {info['msg']} β€” <b>{count}</b> individuals
</div>""",
unsafe_allow_html=True
)
### ----------------- Detailed Analysis Page -------------------
elif page == "Detailed Analysis":
st.title(f"Detailed Analysis (Data Year: {dashboard_year})")
st.header("Correlation Heatmap πŸ”₯")
corr = filtered_df[numeric_cols].corr()
fig, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(corr, annot=True, cmap='coolwarm', ax=ax)
st.pyplot(fig)
st.header("Risk Summary Table πŸ“‹")
risk_summary = pd.DataFrame({
'Category': ['Very High Risk', 'High Chol & TGs', 'High Chol Only', 'High TGs Only', 'Normal'],
'Count': [
((filtered_df['Cholesterol'] > 300) | (filtered_df['Triglycerides'] > 400)).sum(),
((filtered_df['Cholesterol'] > 220) & (filtered_df['Triglycerides'] > 150)).sum(),
((filtered_df['Cholesterol'] > 220) & ~(filtered_df['Triglycerides'] > 150)).sum(),
((filtered_df['Triglycerides'] > 150) & ~(filtered_df['Cholesterol'] > 220)).sum(),
((filtered_df['Cholesterol'] <= 220) & (filtered_df['Triglycerides'] <= 150)).sum()
]
})
st.dataframe(risk_summary)
st.header("Scatter Plot: Cholesterol vs TGs")
risk_category = filtered_df.apply(
lambda row: "Very High Risk" if (row['Cholesterol'] > 300 or row['Triglycerides'] > 400)
else ("High Chol & High TGs" if (row['Cholesterol'] > 220 and row['Triglycerides'] > 150)
else ("High Chol Only" if row['Cholesterol'] > 220 else ("High TGs Only" if row['Triglycerides'] > 150 else "Normal"))),
axis=1
)
fig = px.scatter(
filtered_df,
x="Cholesterol",
y="Triglycerides",
color=risk_category,
trendline="ols",
hover_data=["HbA1c", "UricAcid"]
)
st.plotly_chart(fig)
st.subheader("Other Scatter Plots")
fig1 = px.scatter(filtered_df, x="HbA1c", y="Cholesterol", trendline="ols", title="HbA1c vs Cholesterol")
st.plotly_chart(fig1)
fig2 = px.scatter(filtered_df, x="UricAcid", y="Creatinine", trendline="ols", title="Uric Acid vs Creatinine")
st.plotly_chart(fig2)
fig3 = px.scatter(filtered_df, x="SGPT(ALT)", y="SGOT(AST)", trendline="ols", title="SGPT(ALT) vs SGOT(AST)")
st.plotly_chart(fig3)
# Boxplots
st.subheader("Boxplots for BMI, Weight, and Height")
fig, ax = plt.subplots(1, 3, figsize=(18, 5))
sns.boxplot(y=filtered_df['BMI'], ax=ax[0])
ax[0].set_title("BMI")
sns.boxplot(y=filtered_df['Wg'], ax=ax[1])
ax[1].set_title("Weight (Wg)")
sns.boxplot(y=filtered_df['Hg'], ax=ax[2])
ax[2].set_title("Height (Hg)")
st.pyplot(fig)
# Scatter Plot
st.subheader("Scatter Plot: BMI vs Cholesterol")
st.plotly_chart(px.scatter(filtered_df, x="BMI", y="Cholesterol", trendline="ols", title="BMI vs Cholesterol"))
# Risk Summary by BMI Category
st.subheader("BMI Categories and Risk Levels πŸ§ͺ")
risk_by_bmi = filtered_df.groupby('BMI Category').apply(
lambda x: pd.Series({
'Count': len(x),
'High Cholesterol %': (x['Cholesterol'] > 220).mean() * 100,
'High Triglycerides %': (x['Triglycerides'] > 150).mean() * 100,
'High HbA1c %': (x['HbA1c'] > 6.4).mean() * 100
})
).round(1).reset_index()
st.dataframe(risk_by_bmi)
### ----------------- Advanced Analytics Page -------------------
elif page == "Advanced Analytics":
st.title("πŸš€ Advanced Analytics")
st.info("Coming Soon: Predictive modeling for health risks!")