@@ -20,7 +20,12 @@ def load_data():
2020 if resp .status_code == 200 :
2121 data = resp .json ()
2222 if isinstance (data , list ):
23- return pd .DataFrame (data )
23+ df = pd .DataFrame (data )
24+ # Normalize column names: strip whitespace and ensure common naming
25+ df .columns = [c .strip () for c in df .columns ]
26+ # Fix common encoding issues for temperature
27+ df .columns = [c .replace ('°C' , '°C' ) for c in df .columns ]
28+ return df
2429 st .error (f"Backend Error: { data .get ('error' , 'Unknown' )} " )
2530 except Exception as e :
2631 st .error (f"Failed to connect to backend: { e } " )
@@ -42,14 +47,34 @@ def load_data():
4247 df = full_df
4348 st .success (f"Loaded **{ len (full_df ):,} ** patient records from backend." )
4449
45- # ... rest of the file stays same ...
50+ # ── Safety Check for Column Names ──
51+ available_cols = df .columns .tolist ()
52+ target_x = "Predicted Disease"
53+
54+ if target_x not in available_cols :
55+ # Fallback to similar name if exact match fails
56+ matches = [c for c in available_cols if "Disease" in c or "Condition" in c ]
57+ if matches :
58+ target_x = matches [0 ]
59+ else :
60+ st .error (f"Required column '{ target_x } ' missing from dataset. Found: { available_cols } " )
61+ st .stop ()
62+
4663 st .markdown ("### 🫀 Vital Signs Distribution by Condition" )
47- vitals = ["Heart Rate (bpm)" , "SpO2 Level (%)" , "Systolic Blood Pressure (mmHg)" , "Diastolic Blood Pressure (mmHg)" , "Body Temperature (°C)" ]
64+
65+ # Define possible vitals and filter by what's actually in the DF
66+ vitals_all = ["Heart Rate (bpm)" , "SpO2 Level (%)" , "Systolic Blood Pressure (mmHg)" , "Diastolic Blood Pressure (mmHg)" , "Body Temperature (°C)" ]
67+ vitals = [v for v in vitals_all if v in available_cols ]
68+
69+ if not vitals :
70+ st .error (f"No clinical vital sign columns found in dataset. Found: { available_cols } " )
71+ st .stop ()
72+
4873 selected_vital = st .selectbox ("Select Vital Sign" , vitals )
4974
5075 fig_box = px .box (
51- df , x = "Predicted Disease" , y = selected_vital ,
52- color = "Predicted Disease" ,
76+ df , x = target_x , y = selected_vital ,
77+ color = target_x ,
5378 color_discrete_map = DISEASE_COLORS ,
5479 title = f"{ selected_vital } Distribution Across Conditions" ,
5580 template = "plotly_dark" ,
@@ -67,44 +92,56 @@ def load_data():
6792 c1 , c2 = st .columns (2 )
6893 with c1 :
6994 st .markdown ("##### Heart Rate vs SpO2 (Scatter Matrix)" )
70- fig_scatter = px .scatter (
71- df , x = "Heart Rate (bpm)" , y = "SpO2 Level (%)" ,
72- color = "Predicted Disease" ,
73- color_discrete_map = DISEASE_COLORS ,
74- opacity = 0.4 , size_max = 6 ,
75- title = "Heart Rate vs SpO2 Levels" ,
76- template = "plotly_dark" ,
77- marginal_x = "histogram" ,
78- marginal_y = "histogram"
79- )
80- fig_scatter .update_layout (
81- paper_bgcolor = 'rgba(0,0,0,0)' , plot_bgcolor = 'rgba(0,0,0,0)' ,
82- height = 450 , legend = dict (orientation = "h" , yanchor = "bottom" , y = - 0.35 )
83- )
84- st .plotly_chart (fig_scatter , use_container_width = True )
95+ # Safety check for scatter columns
96+ x_scatter = "Heart Rate (bpm)" if "Heart Rate (bpm)" in available_cols else None
97+ y_scatter = "SpO2 Level (%)" if "SpO2 Level (%)" in available_cols else None
98+
99+ if x_scatter and y_scatter :
100+ fig_scatter = px .scatter (
101+ df , x = x_scatter , y = y_scatter ,
102+ color = target_x ,
103+ color_discrete_map = DISEASE_COLORS ,
104+ opacity = 0.4 , size_max = 6 ,
105+ title = "Heart Rate vs SpO2 Levels" ,
106+ template = "plotly_dark" ,
107+ marginal_x = "histogram" ,
108+ marginal_y = "histogram"
109+ )
110+ fig_scatter .update_layout (
111+ paper_bgcolor = 'rgba(0,0,0,0)' , plot_bgcolor = 'rgba(0,0,0,0)' ,
112+ height = 450 , legend = dict (orientation = "h" , yanchor = "bottom" , y = - 0.35 )
113+ )
114+ st .plotly_chart (fig_scatter , use_container_width = True )
115+ else :
116+ st .warning ("Scatter plot unavailable: Required columns missing." )
85117
86118 with c2 :
87119 st .markdown ("##### Temperature by Condition (Violin Plot)" )
88- fig_violin = px .violin (
89- df , x = "Predicted Disease" , y = "Body Temperature (°C)" ,
90- color = "Predicted Disease" ,
91- color_discrete_map = DISEASE_COLORS ,
92- box = True , points = False ,
93- title = "Body Temperature Distribution" ,
94- template = "plotly_dark"
95- )
96- fig_violin .update_layout (
97- paper_bgcolor = 'rgba(0,0,0,0)' , plot_bgcolor = 'rgba(0,0,0,0)' ,
98- height = 450 , showlegend = False , xaxis_title = "Condition" , yaxis_title = "Temperature (°C)"
99- )
100- st .plotly_chart (fig_violin , use_container_width = True )
120+ y_violin = "Body Temperature (°C)" if "Body Temperature (°C)" in available_cols else None
121+
122+ if y_violin :
123+ fig_violin = px .violin (
124+ df , x = target_x , y = y_violin ,
125+ color = target_x ,
126+ color_discrete_map = DISEASE_COLORS ,
127+ box = True , points = False ,
128+ title = "Body Temperature Distribution" ,
129+ template = "plotly_dark"
130+ )
131+ fig_violin .update_layout (
132+ paper_bgcolor = 'rgba(0,0,0,0)' , plot_bgcolor = 'rgba(0,0,0,0)' ,
133+ height = 450 , showlegend = False , xaxis_title = "Condition" , yaxis_title = "Temperature (°C)"
134+ )
135+ st .plotly_chart (fig_violin , use_container_width = True )
136+ else :
137+ st .warning ("Violin plot unavailable: Temperature column missing." )
101138
102139 st .markdown ("---" )
103140
104141 c3 , c4 = st .columns (2 )
105142 with c3 :
106143 st .markdown ("##### 🏥 Disease Distribution (Population Count)" )
107- counts = df ["Predicted Disease" ].value_counts ().reset_index ()
144+ counts = df [target_x ].value_counts ().reset_index ()
108145 counts .columns = ["Condition" , "Patient Count" ]
109146 fig_bar = px .bar (
110147 counts , x = "Condition" , y = "Patient Count" ,
@@ -123,33 +160,44 @@ def load_data():
123160
124161 with c4 :
125162 st .markdown ("##### 🩸 Blood Pressure Density (Systolic vs Diastolic)" )
126- fig_density = px .density_heatmap (
127- df , x = "Systolic Blood Pressure (mmHg)" , y = "Diastolic Blood Pressure (mmHg)" ,
128- color_continuous_scale = "Turbo" ,
129- title = "Blood Pressure Population Heatmap" ,
130- template = "plotly_dark" ,
131- nbinsx = 30 , nbinsy = 30
132- )
133- fig_density .update_layout (
134- paper_bgcolor = 'rgba(0,0,0,0)' , plot_bgcolor = 'rgba(0,0,0,0)' ,
135- height = 420
136- )
137- st .plotly_chart (fig_density , use_container_width = True )
163+ x_heat = "Systolic Blood Pressure (mmHg)" if "Systolic Blood Pressure (mmHg)" in available_cols else None
164+ y_heat = "Diastolic Blood Pressure (mmHg)" if "Diastolic Blood Pressure (mmHg)" in available_cols else None
165+
166+ if x_heat and y_heat :
167+ fig_density = px .density_heatmap (
168+ df , x = x_heat , y = y_heat ,
169+ color_continuous_scale = "Turbo" ,
170+ title = "Blood Pressure Population Heatmap" ,
171+ template = "plotly_dark" ,
172+ nbinsx = 30 , nbinsy = 30
173+ )
174+ fig_density .update_layout (
175+ paper_bgcolor = 'rgba(0,0,0,0)' , plot_bgcolor = 'rgba(0,0,0,0)' ,
176+ height = 420
177+ )
178+ st .plotly_chart (fig_density , use_container_width = True )
179+ else :
180+ st .warning ("Density heatmap unavailable: Blood Pressure columns missing." )
138181
139182 st .markdown ("---" )
140183 st .markdown ("### 📈 Patient-Specific Real-Time Monitoring" )
141- p_id = st .selectbox ("Select Patient ID to view live telemetry:" , df ['Patient Number' ].unique ()[:50 ])
184+ # Use Patient Number if available
185+ id_col = "Patient Number" if "Patient Number" in available_cols else available_cols [0 ]
186+ p_id = st .selectbox ("Select Patient ID to view live telemetry:" , df [id_col ].unique ()[:50 ])
187+
142188 if p_id :
143- patient_data = df [df ['Patient Number' ] == p_id ].iloc [0 ]
144- disease = patient_data ['Predicted Disease' ]
189+ patient_data = df [df [id_col ] == p_id ].iloc [0 ]
190+ disease = patient_data [target_x ]
145191 d_color = DISEASE_COLORS .get (disease , "#636efa" )
146192 st .markdown (f"**Patient Condition:** <span style='color:{ d_color } ; font-weight:bold'>{ disease } </span>" , unsafe_allow_html = True )
147193
148194 periods = 60
149195 dates = pd .date_range (end = pd .Timestamp .now (), periods = periods , freq = '25s' )
150- base_hr = patient_data ['Heart Rate (bpm)' ]
151- base_spo2 = patient_data ['SpO2 Level (%)' ]
152- base_temp = patient_data ['Body Temperature (°C)' ]
196+
197+ # Safe access for real-time emulation
198+ base_hr = patient_data .get ('Heart Rate (bpm)' , 75 )
199+ base_spo2 = patient_data .get ('SpO2 Level (%)' , 98 )
200+ base_temp = patient_data .get ('Body Temperature (°C)' , 37.0 )
153201 sim_hr = base_hr + np .random .normal (0 , 1.2 , periods ).cumsum ()
154202 sim_spo2 = np .clip (base_spo2 + np .random .normal (0 , 0.3 , periods ).cumsum (), 85 , 100 )
155203 sim_temp = base_temp + np .random .normal (0 , 0.05 , periods ).cumsum ()
0 commit comments