gabcares commited on
Commit
bfc7b8a
·
verified ·
1 Parent(s): c4f561d

Streamlit client source code

Browse files

- streamlit frontend
- Api backend url=https://gabcares-sepsis-fastapi.hf.space/api/v1/prediction?model

.streamlit/config.toml ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ [client]
2
+ showSidebarNavigation = false
Dockerfile ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11.9-slim
2
+
3
+ # Copy requirements file
4
+ COPY requirements.txt .
5
+
6
+ # Update pip
7
+ RUN pip --timeout=3000 install --no-cache-dir --upgrade pip
8
+
9
+ # Install dependecies
10
+ RUN pip --timeout=3000 install --no-cache-dir -r requirements.txt
11
+
12
+ # Make project directory
13
+ RUN mkdir -p /src/client/
14
+
15
+ # Set working directory
16
+ WORKDIR /src/client
17
+
18
+ # Copy client frontend
19
+ COPY . .
20
+
21
+ # Expose app port
22
+ EXPOSE 8501
23
+
24
+ # Start application
25
+ CMD ["streamlit", "run", "app.py"]
app.py ADDED
@@ -0,0 +1,484 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import httpx
4
+ import string
5
+ import random
6
+ import datetime as dt
7
+ from dotenv import load_dotenv
8
+
9
+ import streamlit as st
10
+ import extra_streamlit_components as stx
11
+
12
+ import asyncio
13
+ from aiocache import cached, Cache
14
+
15
+ import pandas as pd
16
+ from typing import Optional, Callable
17
+
18
+ from config import ENV_PATH, BEST_MODELS, TEST_FILE, TEST_FILE_URL, HISTORY_FILE, markdown_table_all
19
+
20
+ from utils.navigation import navigation
21
+ from utils.footer import footer
22
+ from utils.janitor import Janitor
23
+
24
+
25
+ # Load ENV
26
+ load_dotenv(ENV_PATH) # API_URL
27
+
28
+ # Set page configuration
29
+ st.set_page_config(
30
+ page_title="Homepage",
31
+ page_icon="🤖",
32
+ layout="wide",
33
+ initial_sidebar_state='auto'
34
+ )
35
+
36
+
37
+ @cached(ttl=10, cache=Cache.MEMORY, namespace='streamlit_savedataset')
38
+ # @st.cache_data(show_spinner="Saving datasets...") # Streamlit cache is yet to support async functions
39
+ async def save_dataset(df: pd.DataFrame, filepath, csv=True) -> None:
40
+ async def save(df: pd.DataFrame, file):
41
+ return df.to_csv(file, index=False) if csv else df.to_excel(file, index=False)
42
+
43
+ async def read(file):
44
+ return pd.read_csv(file) if csv else pd.read_excel(file)
45
+
46
+ async def same_dfs(df: pd.DataFrame, df2: pd.DataFrame):
47
+ return df.equals(df2)
48
+
49
+ if not os.path.isfile(filepath): # Save if file does not exists
50
+ await save(df, filepath)
51
+ else: # Save if data are not same
52
+ df_old = await read(filepath)
53
+ if not await same_dfs(df, df_old):
54
+ await save(df, filepath)
55
+
56
+
57
+ @cached(ttl=10, cache=Cache.MEMORY, namespace='streamlit_testdata')
58
+ async def get_test_data():
59
+ try:
60
+ df_test_raw = pd.read_csv(TEST_FILE_URL)
61
+ await save_dataset(df_test_raw, TEST_FILE, csv=True)
62
+ except Exception:
63
+ df_test_raw = pd.read_csv(TEST_FILE)
64
+
65
+ # Some house keeping, clean df
66
+ df_test = df_test_raw.copy()
67
+ janitor = Janitor()
68
+ df_test = janitor.clean_dataframe(df_test) # Cleaned
69
+
70
+ return df_test_raw, df_test
71
+
72
+
73
+ # Function for selecting models
74
+ async def select_model() -> str:
75
+ col1, _ = st.columns(2)
76
+ with col1:
77
+ selected_model = st.selectbox(
78
+ 'Select a model', options=BEST_MODELS, key='selected_model')
79
+
80
+ return selected_model
81
+
82
+
83
+ async def endpoint(model: str) -> str:
84
+ api_url = os.getenv("API_URL")
85
+ model_endpoint = f"{api_url}={model}"
86
+ return model_endpoint
87
+
88
+
89
+ # Function for making prediction
90
+ async def make_prediction(model_endpoint) -> Optional[pd.DataFrame]:
91
+
92
+ test_data = await get_test_data()
93
+ _, df_test = test_data
94
+
95
+ df: pd.DataFrame = None
96
+ search_patient = st.session_state.get('search_patient', False)
97
+ search_patient_id = st.session_state.get('search_patient_id', False)
98
+ manual_patient_id = st.session_state.get('manual_patient_id', False)
99
+ if isinstance(search_patient_id, str) and search_patient_id: # And not empty string
100
+ search_patient_id = [search_patient_id]
101
+ if search_patient and search_patient_id: # Search Form df and a patient was selected
102
+ mask = df_test['id'].isin(search_patient_id)
103
+ df_form = df_test[mask]
104
+ df = df_form.copy()
105
+ elif not (search_patient or search_patient_id) and manual_patient_id: # Manual form df
106
+ columns = ['manual_patient_id', 'prg', 'pl', 'pr', 'sk',
107
+ 'ts', 'm11', 'bd2', 'age', 'insurance']
108
+ data = {c: [st.session_state.get(c)] for c in columns}
109
+ data['insurance'] = [1 if i == 'Yes' else 0 for i in data['insurance']]
110
+
111
+ # Make a DataFrame
112
+ df = pd.DataFrame(data).rename(
113
+ columns={'manual_patient_id': 'id'})
114
+ columns_int = ['prg', 'pl', 'pr', 'sk', 'ts', 'age']
115
+ columns_float = ['m11', 'bd2']
116
+
117
+ df[columns_int] = df[columns_int].astype(int)
118
+ df[columns_float] = df[columns_float].astype(float)
119
+ else: # Form did not send a patient
120
+ message = 'You must choose valid patient(s) from the select box.'
121
+ icon = '😞'
122
+ st.toast(message, icon=icon)
123
+ st.warning(message, icon=icon)
124
+
125
+ if df is not None:
126
+ try:
127
+ # JSON data
128
+ data = df.to_dict(orient='list')
129
+
130
+ # Send POST request with JSON data using the json parameter
131
+ async with httpx.AsyncClient() as client:
132
+ response = await client.post(model_endpoint, json=data, timeout=30)
133
+ response.raise_for_status() # Ensure we catch any HTTP errors
134
+
135
+ if (response.status_code == 200):
136
+ pred_prob = (response.json()['result'])
137
+ prediction = pred_prob['prediction'][0]
138
+ probability = pred_prob['probability'][0]
139
+
140
+ # Store results in session state
141
+ st.session_state['prediction'] = prediction
142
+ st.session_state['probability'] = probability
143
+ df['prediction'] = prediction
144
+ df['probability (%)'] = probability
145
+ df['time_of_prediction'] = pd.Timestamp(dt.datetime.now())
146
+ df['model_used'] = st.session_state['selected_model']
147
+
148
+ df.to_csv(HISTORY_FILE, mode='a',
149
+ header=not os.path.isfile(HISTORY_FILE))
150
+ except Exception as e:
151
+ st.error(f'😞 Unable to connect to the API server. {e}')
152
+
153
+ return df
154
+
155
+
156
+ async def convert_string(df: pd.DataFrame, string: str) -> str:
157
+ return string.upper() if all(col.isupper() for col in df.columns) else string
158
+
159
+
160
+ async def make_predictions(model_endpoint, df_uploaded=None, df_uploaded_clean=None) -> Optional[pd.DataFrame]:
161
+
162
+ df: pd.DataFrame = None
163
+ search_patient = st.session_state.get('search_patient', False)
164
+ patient_id_bulk = st.session_state.get('patient_id_bulk', False)
165
+ upload_bulk_predict = st.session_state.get('upload_bulk_predict', False)
166
+ if search_patient and patient_id_bulk: # Search Form df and a patient was selected
167
+ _, df_test = await get_test_data()
168
+ mask = df_test['id'].isin(patient_id_bulk)
169
+ df_bulk: pd.DataFrame = df_test[mask]
170
+ df = df_bulk.copy()
171
+
172
+ elif not (search_patient or patient_id_bulk) and upload_bulk_predict: # Upload widget df
173
+ df = df_uploaded_clean.copy()
174
+ else: # Form did not send a patient
175
+ message = 'You must choose valid patient(s) from the select box.'
176
+ icon = '😞'
177
+ st.toast(message, icon=icon)
178
+ st.warning(message, icon=icon)
179
+
180
+ if df is not None: # df should be set by form input or upload widget
181
+ try:
182
+ # JSON data
183
+ data = df.to_dict(orient='list')
184
+
185
+ # Send POST request with JSON data using the json parameter
186
+ async with httpx.AsyncClient() as client:
187
+ response = await client.post(model_endpoint, json=data, timeout=30)
188
+ response.raise_for_status() # Ensure we catch any HTTP errors
189
+
190
+ if (response.status_code == 200):
191
+ pred_prob = (response.json()['result'])
192
+ predictions = pred_prob['prediction']
193
+ probabilities = pred_prob['probability']
194
+
195
+ # Add columns sepsis, probability, time, and model used to uploaded df and form df
196
+
197
+ async def add_columns(df):
198
+ df[await convert_string(df, 'sepsis')] = predictions
199
+ df[await convert_string(df, 'probability_(%)')] = probabilities
200
+ df[await convert_string(df, 'time_of_prediction')
201
+ ] = pd.Timestamp(dt.datetime.now())
202
+ df[await convert_string(df, 'model_used')
203
+ ] = st.session_state['selected_model']
204
+
205
+ return df
206
+
207
+ # Form df if search patient is true or df from Uploaded data
208
+ if search_patient:
209
+ df = await add_columns(df)
210
+
211
+ df.to_csv(HISTORY_FILE, mode='a', header=not os.path.isfile(
212
+ HISTORY_FILE)) # Save only known patients
213
+
214
+ else:
215
+ df = await add_columns(df_uploaded) # Raw, No cleaning
216
+
217
+ # Store df with prediction results in session state
218
+ st.session_state['bulk_prediction_df'] = df
219
+ except Exception as e:
220
+ st.error(f'😞 Unable to connect to the API server. {e}')
221
+
222
+ return df
223
+
224
+
225
+ def on_click(func: Callable, model_endpoint: str):
226
+ async def handle_click():
227
+ await func(model_endpoint)
228
+
229
+ loop = asyncio.new_event_loop()
230
+ asyncio.set_event_loop(loop)
231
+ loop.run_until_complete(handle_click())
232
+ loop.close()
233
+
234
+
235
+ async def search_patient_form(model_endpoint: str) -> None:
236
+ test_data = await get_test_data()
237
+ _, df_test = test_data
238
+
239
+ patient_ids = df_test['id'].unique().tolist()+['']
240
+ if st.session_state['sidebar'] == 'single_prediction':
241
+ with st.form('search_patient_id_form'):
242
+ col1, _ = st.columns(2)
243
+ with col1:
244
+ st.write('#### Patient ID 🤒')
245
+ st.selectbox(
246
+ 'Search a patient', options=patient_ids, index=len(patient_ids)-1, key='search_patient_id')
247
+ st.form_submit_button('Predict', type='primary', on_click=on_click, kwargs=dict(
248
+ func=make_prediction, model_endpoint=model_endpoint))
249
+ else:
250
+ with st.form('search_patient_id_bulk_form'):
251
+ col1, _ = st.columns(2)
252
+ with col1:
253
+ st.write('#### Patient ID 🤒')
254
+ st.multiselect(
255
+ 'Search a patient', options=patient_ids, default=None, key='patient_id_bulk')
256
+ st.form_submit_button('Predict', type='primary', on_click=on_click, kwargs=dict(
257
+ func=make_predictions, model_endpoint=model_endpoint))
258
+
259
+
260
+ async def gen_random_patient_id() -> str:
261
+ numbers = ''.join(random.choices(string.digits, k=6))
262
+ letters = ''.join(random.choices(string.ascii_lowercase, k=4))
263
+ return f"ICU{numbers}-gen-{letters}"
264
+
265
+
266
+ async def manual_patient_form(model_endpoint) -> None:
267
+ with st.form('manual_patient_form'):
268
+
269
+ col1, col2, col3 = st.columns(3)
270
+
271
+ with col1:
272
+ st.write('### Patient Demographics 🛌')
273
+ st.text_input(
274
+ 'ID', value=await gen_random_patient_id(), key='manual_patient_id')
275
+ st.number_input('Age: patients age (years)', min_value=0,
276
+ max_value=100, step=1, key='age')
277
+ st.selectbox('Insurance: If a patient holds a valid insurance card', options=[
278
+ 'Yes', 'No'], key='insurance')
279
+
280
+ with col2:
281
+ st.write('### Vital Signs 🩺')
282
+ st.number_input('BMI (weight in kg/(height in m)^2', min_value=10.0,
283
+ format="%.2f", step=1.00, key='m11')
284
+ st.number_input(
285
+ 'Blood Pressure (mm Hg)', min_value=10.0, format="%.2f", step=1.00, key='pr')
286
+ st.number_input(
287
+ 'PRG (plasma glucose)', min_value=10.0, format="%.2f", step=1.00, key='prg')
288
+
289
+ with col3:
290
+ st.write('### Blood Work 💉')
291
+ st.number_input(
292
+ 'PL: Blood Work Result-1 (mu U/ml)', min_value=10.0, format="%.2f", step=1.00, key='pl')
293
+ st.number_input(
294
+ 'SK: Blood Work Result 2 (mm)', min_value=10.0, format="%.2f", step=1.00, key='sk')
295
+ st.number_input(
296
+ 'TS: Blood Work Result-3 (mu U/ml)', min_value=10.0, format="%.2f", step=1.00, key='ts')
297
+ st.number_input(
298
+ 'BD2: Blood Work Result-4 (mu U/ml)', min_value=10.0, format="%.2f", step=1.00, key='bd2')
299
+
300
+ st.form_submit_button('Predict', type='primary', on_click=on_click, kwargs=dict(
301
+ func=make_prediction, model_endpoint=model_endpoint))
302
+
303
+
304
+ async def do_single_prediction(model_endpoint: str) -> None:
305
+ if st.session_state.get('search_patient', False):
306
+ await search_patient_form(model_endpoint)
307
+ else:
308
+ await manual_patient_form(model_endpoint)
309
+
310
+
311
+ async def show_prediction() -> None:
312
+ final_prediction = st.session_state.get('prediction', None)
313
+ final_probability = st.session_state.get('probability', None)
314
+
315
+ if final_prediction is None:
316
+ st.markdown('#### Prediction will show below! 🔬')
317
+ st.divider()
318
+ else:
319
+ st.markdown('#### Prediction! 🔬')
320
+ st.divider()
321
+ if final_prediction.lower() == 'positive':
322
+ st.toast("Sepsis alert!", icon='🦠')
323
+ message = f"It is **{final_probability:.2f} %** likely that the patient will develop **sepsis.**"
324
+ st.warning(message, icon='😞')
325
+ time.sleep(5)
326
+ st.toast(message)
327
+ else:
328
+ st.toast("Continous monitoring", icon='🔬')
329
+ message = f"The patient will **not** develop sepsis with a likelihood of **{final_probability:.2f}%**."
330
+ st.success(message, icon='😊')
331
+ time.sleep(5)
332
+ st.toast(message)
333
+
334
+ # Set prediction and probability to None
335
+ st.session_state['prediction'] = None
336
+ st.session_state['probability'] = None
337
+
338
+
339
+ # @st.cache_data(show_spinner=False) Caching results from async functions buggy
340
+ async def convert_df(df: pd.DataFrame):
341
+ return df.to_csv(index=False)
342
+
343
+
344
+ async def bulk_upload_widget(model_endpoint: str) -> None:
345
+ uploaded_file = st.file_uploader(
346
+ "Choose a CSV or Excel File", type=['csv', 'xls', 'xlsx'])
347
+
348
+ uploaded = uploaded_file is not None
349
+
350
+ upload_bulk_predict = st.button('Predict', type='primary',
351
+ help='Upload a csv/excel file to make predictions', disabled=not uploaded, key='upload_bulk_predict')
352
+ df = None
353
+ if upload_bulk_predict and uploaded:
354
+ df_test_raw, _ = await get_test_data()
355
+ # Uploadfile is a "file-like" object is accepted
356
+ try:
357
+ try:
358
+ df = pd.read_csv(uploaded_file)
359
+ except Exception:
360
+ df = pd.read_excel(uploaded_file)
361
+
362
+ df_columns = set(df.columns)
363
+ df_test_columns = set(df_test_raw.columns)
364
+ df_schema = df.dtypes
365
+ df_test_schema = df_test_raw.dtypes
366
+
367
+ if df_columns != df_test_columns or not df_schema.equals(df_test_schema):
368
+ df = None
369
+ raise Exception
370
+ else:
371
+ # Clean dataframe
372
+ janitor = Janitor()
373
+ df_clean = janitor.clean_dataframe(df)
374
+
375
+ df = await make_predictions(
376
+ model_endpoint, df_uploaded=df, df_uploaded_clean=df_clean)
377
+
378
+ except Exception:
379
+ st.subheader('Data template')
380
+ data_template = df_test_raw[:3]
381
+ st.dataframe(data_template)
382
+ csv = await convert_df(data_template)
383
+ message_1 = 'Upload a valid csv or excel file.'
384
+ message_2 = f"{message_1.split('.')[0]} with the columns and schema of the above data template."
385
+ icon = '😞'
386
+ st.toast(message_1, icon=icon)
387
+
388
+ st.download_button(
389
+ label='Download template',
390
+ data=csv,
391
+ file_name='Data template.csv',
392
+ mime="text/csv",
393
+ type='secondary',
394
+ key='download-data-template'
395
+ )
396
+ st.info('Download the above template for use as a baseline structure.')
397
+
398
+ # Display explander to show the data dictionary
399
+ with st.expander("Expand to see the data dictionary", icon="💡"):
400
+ st.subheader("Data dictionary")
401
+ st.markdown(markdown_table_all)
402
+ st.warning(message_2, icon=icon)
403
+
404
+ return df
405
+
406
+
407
+ async def do_bulk_prediction(model_endpoint: str) -> None:
408
+ if st.session_state.get('search_patient', False):
409
+ await search_patient_form(model_endpoint)
410
+ else:
411
+ # File uploader
412
+ await bulk_upload_widget(model_endpoint)
413
+
414
+
415
+ async def show_bulk_predictions(df: pd.DataFrame) -> None:
416
+ if df is not None:
417
+ st.subheader("Bulk predictions 🔮", divider=True)
418
+ st.dataframe(df.astype(str))
419
+
420
+ csv = await convert_df(df)
421
+ message = 'The predictions are ready for download.'
422
+ icon = '⬇️'
423
+ st.toast(message, icon=icon)
424
+ st.info(message, icon=icon)
425
+ st.download_button(
426
+ label='Download predictions',
427
+ data=csv,
428
+ file_name='Bulk prediction.csv',
429
+ mime="text/csv",
430
+ type='secondary',
431
+ key='download-bulk-prediction'
432
+ )
433
+
434
+ # Set bulk prediction df to None
435
+ st.session_state['bulk_prediction_df'] = None
436
+
437
+
438
+ async def sidebar(sidebar_type: str) -> st.sidebar:
439
+ return st.session_state.update({'sidebar': sidebar_type})
440
+
441
+
442
+ async def main():
443
+ st.title("🤖 Predict Sepsis 🦠")
444
+
445
+ # Navigation
446
+ await navigation()
447
+
448
+ st.sidebar.toggle("Looking for a patient?", value=st.session_state.get(
449
+ 'search_patient', False), key='search_patient')
450
+
451
+ selected_model = await select_model()
452
+ model_endpoint = await endpoint(selected_model)
453
+
454
+ selected_predict_tab = st.session_state.get('selected_predict_tab')
455
+ default = 1 if selected_predict_tab is None else selected_predict_tab
456
+
457
+ with st.spinner('A little house keeping...'):
458
+ time.sleep(st.session_state.get('sleep', 1.5))
459
+ chosen_id = stx.tab_bar(data=[
460
+ stx.TabBarItemData(id=1, title='🔬 Predict', description=''),
461
+ stx.TabBarItemData(id=2, title='🔮 Bulk predict',
462
+ description=''),
463
+ ], default=default)
464
+ st.session_state['sleep'] = 0
465
+
466
+ if chosen_id == '1':
467
+ await sidebar('single_prediction')
468
+ await do_single_prediction(model_endpoint)
469
+ await show_prediction()
470
+
471
+ elif chosen_id == '2':
472
+ await sidebar('bulk_prediction')
473
+ df_with_predictions = await do_bulk_prediction(model_endpoint)
474
+ if df_with_predictions is None:
475
+ df_with_predictions = st.session_state.get(
476
+ 'bulk_prediction_df', None)
477
+ await show_bulk_predictions(df_with_predictions)
478
+
479
+ # Add footer
480
+ await footer()
481
+
482
+
483
+ if __name__ == "__main__":
484
+ asyncio.run(main())
config.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pathlib import Path
3
+
4
+ # Paths
5
+ BASE_DIR = './'
6
+ DATA = os.path.join(BASE_DIR, 'data/')
7
+ TEST_FILE = os.path.join(DATA, 'Paitients_Files_Test.csv')
8
+ HISTORY = os.path.join(DATA, 'history/')
9
+ HISTORY_FILE = os.path.join(HISTORY, 'history.csv')
10
+
11
+ # Urls
12
+ TEST_FILE_URL = "https://raw.githubusercontent.com/D0nG4667/sepsis_prediction_full_stack/model_development/dev/data/Paitients_Files_Test.csv"
13
+
14
+
15
+ # ENV when using standalone streamlit server
16
+ ENV_PATH = Path('../../env/online.env')
17
+
18
+ ALL_MODELS = [
19
+ "AdaBoostClassifier",
20
+ "CatBoostClassifier",
21
+ "DecisionTreeClassifier",
22
+ "KNeighborsClassifier",
23
+ "LGBMClassifier",
24
+ "LogisticRegression",
25
+ "RandomForestClassifier",
26
+ "SupportVectorClassifier",
27
+ "XGBoostClassifier",
28
+ ]
29
+
30
+ BEST_MODELS = ["RandomForestClassifier", "XGBoostClassifier"]
31
+
32
+ markdown_table_all = """
33
+ | Column Name | Attribute/Target | Description |
34
+ |------------------------------|------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
35
+ | ID | N/A | Unique number to represent patient ID |
36
+ | PRG | Attribute1 | Plasma glucose|
37
+ | PL | Attribute 2 | Blood Work Result-1 (mu U/ml) |
38
+ | PR | Attribute 3 | Blood Pressure (mm Hg)|
39
+ | SK | Attribute 4 | Blood Work Result-2 (mm)|
40
+ | TS | Attribute 5 | Blood Work Result-3 (mu U/ml)|
41
+ | M11 | Attribute 6 | Body mass index (weight in kg/(height in m)^2|
42
+ | BD2 | Attribute 7 | Blood Work Result-4 (mu U/ml)|
43
+ | Age | Attribute 8 | patients age (years)|
44
+ | Insurance | N/A | If a patient holds a valid insurance card|
45
+ | Sepsis | Target | Positive: if a patient in ICU will develop a sepsis , and Negative: otherwise |
46
+ """
data/Paitients_Files_Test.csv ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ID,PRG,PL,PR,SK,TS,M11,BD2,Age,Insurance
2
+ ICU200609,1,109,38,18,120,23.1,0.407,26,1
3
+ ICU200610,1,108,88,19,0,27.1,0.4,24,1
4
+ ICU200611,6,96,0,0,0,23.7,0.19,28,1
5
+ ICU200612,1,124,74,36,0,27.8,0.1,30,1
6
+ ICU200613,7,150,78,29,126,35.2,0.692,54,0
7
+ ICU200614,4,183,0,0,0,28.4,0.212,36,1
8
+ ICU200615,1,124,60,32,0,35.8,0.514,21,1
9
+ ICU200616,1,181,78,42,293,40.0,1.258,22,1
10
+ ICU200617,1,92,62,25,41,19.5,0.482,25,0
11
+ ICU200618,0,152,82,39,272,41.5,0.27,27,0
12
+ ICU200619,1,111,62,13,182,24.0,0.138,23,1
13
+ ICU200620,3,106,54,21,158,30.9,0.292,24,1
14
+ ICU200621,3,174,58,22,194,32.9,0.593,36,1
15
+ ICU200622,7,168,88,42,321,38.2,0.787,40,1
16
+ ICU200623,6,105,80,28,0,32.5,0.878,26,1
17
+ ICU200624,11,138,74,26,144,36.1,0.557,50,1
18
+ ICU200625,3,106,72,0,0,25.8,0.207,27,1
19
+ ICU200626,6,117,96,0,0,28.7,0.157,30,1
20
+ ICU200627,2,68,62,13,15,20.1,0.257,23,0
21
+ ICU200628,9,112,82,24,0,28.2,1.282,50,1
22
+ ICU200629,0,119,0,0,0,32.4,0.141,24,1
23
+ ICU200630,2,112,86,42,160,38.4,0.246,28,1
24
+ ICU200631,2,92,76,20,0,24.2,1.698,28,1
25
+ ICU200632,6,183,94,0,0,40.8,1.461,45,0
26
+ ICU200633,0,94,70,27,115,43.5,0.347,21,1
27
+ ICU200634,2,108,64,0,0,30.8,0.158,21,1
28
+ ICU200635,4,90,88,47,54,37.7,0.362,29,1
29
+ ICU200636,0,125,68,0,0,24.7,0.206,21,1
30
+ ICU200637,0,132,78,0,0,32.4,0.393,21,1
31
+ ICU200638,5,128,80,0,0,34.6,0.144,45,1
32
+ ICU200639,4,94,65,22,0,24.7,0.148,21,1
33
+ ICU200640,7,114,64,0,0,27.4,0.732,34,1
34
+ ICU200641,0,102,78,40,90,34.5,0.238,24,0
35
+ ICU200642,2,111,60,0,0,26.2,0.343,23,0
36
+ ICU200643,1,128,82,17,183,27.5,0.115,22,0
37
+ ICU200644,10,92,62,0,0,25.9,0.167,31,0
38
+ ICU200645,13,104,72,0,0,31.2,0.465,38,0
39
+ ICU200646,5,104,74,0,0,28.8,0.153,48,0
40
+ ICU200647,2,94,76,18,66,31.6,0.649,23,0
41
+ ICU200648,7,97,76,32,91,40.9,0.871,32,0
42
+ ICU200649,1,100,74,12,46,19.5,0.149,28,0
43
+ ICU200650,0,102,86,17,105,29.3,0.695,27,1
44
+ ICU200651,4,128,70,0,0,34.3,0.303,24,1
45
+ ICU200652,6,147,80,0,0,29.5,0.178,50,1
46
+ ICU200653,4,90,0,0,0,28.0,0.61,31,1
47
+ ICU200654,3,103,72,30,152,27.6,0.73,27,1
48
+ ICU200655,2,157,74,35,440,39.4,0.134,30,1
49
+ ICU200656,1,167,74,17,144,23.4,0.447,33,0
50
+ ICU200657,0,179,50,36,159,37.8,0.455,22,0
51
+ ICU200658,11,136,84,35,130,28.3,0.26,42,0
52
+ ICU200659,0,107,60,25,0,26.4,0.133,23,1
53
+ ICU200660,1,91,54,25,100,25.2,0.234,23,0
54
+ ICU200661,1,117,60,23,106,33.8,0.466,27,0
55
+ ICU200662,5,123,74,40,77,34.1,0.269,28,0
56
+ ICU200663,2,120,54,0,0,26.8,0.455,27,1
57
+ ICU200664,1,106,70,28,135,34.2,0.142,22,1
58
+ ICU200665,2,155,52,27,540,38.7,0.24,25,0
59
+ ICU200666,2,101,58,35,90,21.8,0.155,22,1
60
+ ICU200667,1,120,80,48,200,38.9,1.162,41,1
61
+ ICU200668,11,127,106,0,0,39.0,0.19,51,1
62
+ ICU200669,3,80,82,31,70,34.2,1.292,27,1
63
+ ICU200670,10,162,84,0,0,27.7,0.182,54,0
64
+ ICU200671,1,199,76,43,0,42.9,1.394,22,1
65
+ ICU200672,8,167,106,46,231,37.6,0.165,43,1
66
+ ICU200673,9,145,80,46,130,37.9,0.637,40,1
67
+ ICU200674,6,115,60,39,0,33.7,0.245,40,1
68
+ ICU200675,1,112,80,45,132,34.8,0.217,24,1
69
+ ICU200676,4,145,82,18,0,32.5,0.235,70,1
70
+ ICU200677,10,111,70,27,0,27.5,0.141,40,1
71
+ ICU200678,6,98,58,33,190,34.0,0.43,43,1
72
+ ICU200679,9,154,78,30,100,30.9,0.164,45,1
73
+ ICU200680,6,165,68,26,168,33.6,0.631,49,1
74
+ ICU200681,1,99,58,10,0,25.4,0.551,21,1
75
+ ICU200682,10,68,106,23,49,35.5,0.285,47,1
76
+ ICU200683,3,123,100,35,240,57.3,0.88,22,0
77
+ ICU200684,8,91,82,0,0,35.6,0.587,68,0
78
+ ICU200685,6,195,70,0,0,30.9,0.328,31,0
79
+ ICU200686,9,156,86,0,0,24.8,0.23,53,1
80
+ ICU200687,0,93,60,0,0,35.3,0.263,25,1
81
+ ICU200688,3,121,52,0,0,36.0,0.127,25,1
82
+ ICU200689,2,101,58,17,265,24.2,0.614,23,1
83
+ ICU200690,2,56,56,28,45,24.2,0.332,22,0
84
+ ICU200691,0,162,76,36,0,49.6,0.364,26,1
85
+ ICU200692,0,95,64,39,105,44.6,0.366,22,1
86
+ ICU200693,4,125,80,0,0,32.3,0.536,27,1
87
+ ICU200694,5,136,82,0,0,0.0,0.64,69,1
88
+ ICU200695,2,129,74,26,205,33.2,0.591,25,1
89
+ ICU200696,3,130,64,0,0,23.1,0.314,22,1
90
+ ICU200697,1,107,50,19,0,28.3,0.181,29,1
91
+ ICU200698,1,140,74,26,180,24.1,0.828,23,1
92
+ ICU200699,1,144,82,46,180,46.1,0.335,46,1
93
+ ICU200700,8,107,80,0,0,24.6,0.856,34,1
94
+ ICU200701,13,158,114,0,0,42.3,0.257,44,1
95
+ ICU200702,2,121,70,32,95,39.1,0.886,23,1
96
+ ICU200703,7,129,68,49,125,38.5,0.439,43,0
97
+ ICU200704,2,90,60,0,0,23.5,0.191,25,0
98
+ ICU200705,7,142,90,24,480,30.4,0.128,43,0
99
+ ICU200706,3,169,74,19,125,29.9,0.268,31,1
100
+ ICU200707,0,99,0,0,0,25.0,0.253,22,1
101
+ ICU200708,4,127,88,11,155,34.5,0.598,28,1
102
+ ICU200709,4,118,70,0,0,44.5,0.904,26,1
103
+ ICU200710,2,122,76,27,200,35.9,0.483,26,0
104
+ ICU200711,6,125,78,31,0,27.6,0.565,49,1
105
+ ICU200712,1,168,88,29,0,35.0,0.905,52,1
106
+ ICU200713,2,129,0,0,0,38.5,0.304,41,1
107
+ ICU200714,4,110,76,20,100,28.4,0.118,27,1
108
+ ICU200715,6,80,80,36,0,39.8,0.177,28,1
109
+ ICU200716,10,115,0,0,0,0.0,0.261,30,1
110
+ ICU200717,2,127,46,21,335,34.4,0.176,22,1
111
+ ICU200718,9,164,78,0,0,32.8,0.148,45,1
112
+ ICU200719,2,93,64,32,160,38.0,0.674,23,1
113
+ ICU200720,3,158,64,13,387,31.2,0.295,24,1
114
+ ICU200721,5,126,78,27,22,29.6,0.439,40,1
115
+ ICU200722,10,129,62,36,0,41.2,0.441,38,1
116
+ ICU200723,0,134,58,20,291,26.4,0.352,21,0
117
+ ICU200724,3,102,74,0,0,29.5,0.121,32,0
118
+ ICU200725,7,187,50,33,392,33.9,0.826,34,0
119
+ ICU200726,3,173,78,39,185,33.8,0.97,31,1
120
+ ICU200727,10,94,72,18,0,23.1,0.595,56,1
121
+ ICU200728,1,108,60,46,178,35.5,0.415,24,1
122
+ ICU200729,5,97,76,27,0,35.6,0.378,52,1
123
+ ICU200730,4,83,86,19,0,29.3,0.317,34,0
124
+ ICU200731,1,114,66,36,200,38.1,0.289,21,0
125
+ ICU200732,1,149,68,29,127,29.3,0.349,42,0
126
+ ICU200733,5,117,86,30,105,39.1,0.251,42,0
127
+ ICU200734,1,111,94,0,0,32.8,0.265,45,0
128
+ ICU200735,4,112,78,40,0,39.4,0.236,38,0
129
+ ICU200736,1,116,78,29,180,36.1,0.496,25,0
130
+ ICU200737,0,141,84,26,0,32.4,0.433,22,0
131
+ ICU200738,2,175,88,0,0,22.9,0.326,22,0
132
+ ICU200739,2,92,52,0,0,30.1,0.141,22,1
133
+ ICU200740,3,130,78,23,79,28.4,0.323,34,1
134
+ ICU200741,8,120,86,0,0,28.4,0.259,22,1
135
+ ICU200742,2,174,88,37,120,44.5,0.646,24,1
136
+ ICU200743,2,106,56,27,165,29.0,0.426,22,1
137
+ ICU200744,2,105,75,0,0,23.3,0.56,53,1
138
+ ICU200745,4,95,60,32,0,35.4,0.284,28,1
139
+ ICU200746,0,126,86,27,120,27.4,0.515,21,1
140
+ ICU200747,8,65,72,23,0,32.0,0.6,42,1
141
+ ICU200748,2,99,60,17,160,36.6,0.453,21,1
142
+ ICU200749,1,102,74,0,0,39.5,0.293,42,1
143
+ ICU200750,11,120,80,37,150,42.3,0.785,48,1
144
+ ICU200751,3,102,44,20,94,30.8,0.4,26,1
145
+ ICU200752,1,109,58,18,116,28.5,0.219,22,1
146
+ ICU200753,9,140,94,0,0,32.7,0.734,45,1
147
+ ICU200754,13,153,88,37,140,40.6,1.174,39,1
148
+ ICU200755,12,100,84,33,105,30.0,0.488,46,0
149
+ ICU200756,1,147,94,41,0,49.3,0.358,27,0
150
+ ICU200757,1,81,74,41,57,46.3,1.096,32,1
151
+ ICU200758,3,187,70,22,200,36.4,0.408,36,1
152
+ ICU200759,6,162,62,0,0,24.3,0.178,50,1
153
+ ICU200760,4,136,70,0,0,31.2,1.182,22,1
154
+ ICU200761,1,121,78,39,74,39.0,0.261,28,1
155
+ ICU200762,3,108,62,24,0,26.0,0.223,25,1
156
+ ICU200763,0,181,88,44,510,43.3,0.222,26,1
157
+ ICU200764,8,154,78,32,0,32.4,0.443,45,1
158
+ ICU200765,1,128,88,39,110,36.5,1.057,37,1
159
+ ICU200766,7,137,90,41,0,32.0,0.391,39,0
160
+ ICU200767,0,123,72,0,0,36.3,0.258,52,1
161
+ ICU200768,1,106,76,0,0,37.5,0.197,26,1
162
+ ICU200769,6,190,92,0,0,35.5,0.278,66,0
163
+ ICU200770,2,88,58,26,16,28.4,0.766,22,1
164
+ ICU200771,9,170,74,31,0,44.0,0.403,43,1
165
+ ICU200772,9,89,62,0,0,22.5,0.142,33,1
166
+ ICU200773,10,101,76,48,180,32.9,0.171,63,1
167
+ ICU200774,2,122,70,27,0,36.8,0.34,27,1
168
+ ICU200775,5,121,72,23,112,26.2,0.245,30,1
169
+ ICU200776,1,126,60,0,0,30.1,0.349,47,1
170
+ ICU200777,1,93,70,31,0,30.4,0.315,23,1
data/history/history.csv ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ,id,prg,pl,pr,sk,ts,m11,bd2,age,insurance,prediction,probability (%),time_of_prediction,model_used
2
+ 0,ICU718842-gen-nlwl,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 09:15:06.306895,RandomForestClassifier
3
+ 0,ICU639505-gen-yfab,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 09:16:27.459841,RandomForestClassifier
4
+ 0,ICU195521-gen-hvkd,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 09:16:31.500162,RandomForestClassifier
5
+ 0,ICU387670-gen-yumn,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 09:17:16.749320,RandomForestClassifier
6
+ 0,ICU982466-gen-iczo,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 09:17:53.997299,RandomForestClassifier
7
+ 0,ICU907863-gen-mrwx,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 09:20:04.840719,RandomForestClassifier
8
+ 2,ICU200611,6,96,0,0,0,23.7,0.19,28,1,Negative,96.0,2024-07-26 09:23:42.879892,RandomForestClassifier
9
+ 0,ICU652368-gen-mczz,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 09:38:19.827792,RandomForestClassifier
10
+ 0,ICU630077-gen-hmcu,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 09:38:23.524249,RandomForestClassifier
11
+ 0,ICU860259-gen-gxvq,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 17:13:49.802527,RandomForestClassifier
12
+ 6,ICU200615,1,124,60,32,0,35.8,0.514,21,1,Negative,79.0,2024-07-26 17:14:16.407693,RandomForestClassifier
13
+ 2,ICU200611,6,96,0,0,0,23.7,0.19,28,1,Negative,96.0,2024-07-26 17:14:35.174256,RandomForestClassifier
14
+ 4,ICU200613,7,150,78,29,126,35.2,0.692,54,0,Positive,63.0,2024-07-26 17:14:35.174256,RandomForestClassifier
15
+ 7,ICU200616,1,181,78,42,293,40.0,1.258,22,1,Positive,56.0,2024-07-26 17:14:35.174256,RandomForestClassifier
16
+ 8,ICU200617,1,92,62,25,41,19.5,0.482,25,0,Negative,99.0,2024-07-26 17:14:35.174256,RandomForestClassifier
17
+ 9,ICU200618,0,152,82,39,272,41.5,0.27,27,0,Negative,73.0,2024-07-26 17:14:35.174256,RandomForestClassifier
18
+ 10,ICU200619,1,111,62,13,182,24.0,0.138,23,1,Negative,100.0,2024-07-26 17:14:35.174256,RandomForestClassifier
19
+ 0,ICU857889-gen-akic,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 17:36:06.901881,RandomForestClassifier
20
+ 1,ICU200610,1,108,88,19,0,27.1,0.4,24,1,Negative,88.0,2024-07-26 17:36:25.138350,RandomForestClassifier
21
+ 2,ICU200611,6,96,0,0,0,23.7,0.19,28,1,Negative,96.0,2024-07-26 17:36:51.337836,RandomForestClassifier
22
+ 4,ICU200613,7,150,78,29,126,35.2,0.692,54,0,Positive,63.0,2024-07-26 17:36:51.337836,RandomForestClassifier
23
+ 6,ICU200615,1,124,60,32,0,35.8,0.514,21,1,Negative,79.0,2024-07-26 17:36:51.337836,RandomForestClassifier
24
+ 7,ICU200616,1,181,78,42,293,40.0,1.258,22,1,Positive,56.0,2024-07-26 17:36:51.337836,RandomForestClassifier
25
+ 8,ICU200617,1,92,62,25,41,19.5,0.482,25,0,Negative,99.0,2024-07-26 17:36:51.337836,RandomForestClassifier
26
+ 9,ICU200618,0,152,82,39,272,41.5,0.27,27,0,Negative,73.0,2024-07-26 17:36:51.337836,RandomForestClassifier
27
+ 0,ICU335499-gen-perk,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 18:06:45.472828,RandomForestClassifier
28
+ 0,ICU547969-gen-zvtk,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 18:07:08.106437,RandomForestClassifier
29
+ 1,ICU200610,1,108,88,19,0,27.1,0.4,24,1,Negative,88.0,2024-07-26 18:07:28.070427,RandomForestClassifier
30
+ 3,ICU200612,1,124,74,36,0,27.8,0.1,30,1,Negative,69.0,2024-07-26 18:07:28.070427,RandomForestClassifier
31
+ 5,ICU200614,4,183,0,0,0,28.4,0.212,36,1,Positive,52.0,2024-07-26 18:07:28.070427,RandomForestClassifier
32
+ 7,ICU200616,1,181,78,42,293,40.0,1.258,22,1,Positive,56.0,2024-07-26 18:07:28.070427,RandomForestClassifier
33
+ 8,ICU200617,1,92,62,25,41,19.5,0.482,25,0,Negative,99.0,2024-07-26 18:07:28.070427,RandomForestClassifier
34
+ 9,ICU200618,0,152,82,39,272,41.5,0.27,27,0,Negative,73.0,2024-07-26 18:07:28.070427,RandomForestClassifier
35
+ 0,ICU930006-gen-yifq,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 20:27:22.413639,RandomForestClassifier
36
+ 1,ICU200610,1,108,88,19,0,27.1,0.4,24,1,Negative,88.0,2024-07-26 20:29:38.351280,RandomForestClassifier
37
+ 1,ICU200610,1,108,88,19,0,27.1,0.4,24,1,Negative,88.0,2024-07-26 20:29:53.169096,RandomForestClassifier
38
+ 0,ICU766249-gen-ojbw,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 20:30:20.584075,RandomForestClassifier
39
+ 1,ICU200610,1,108,88,19,0,27.1,0.4,24,1,Negative,88.0,2024-07-26 20:30:41.673970,RandomForestClassifier
40
+ 4,ICU200613,7,150,78,29,126,35.2,0.692,54,0,Positive,63.0,2024-07-26 20:30:41.673970,RandomForestClassifier
41
+ 6,ICU200615,1,124,60,32,0,35.8,0.514,21,1,Negative,79.0,2024-07-26 20:30:41.673970,RandomForestClassifier
42
+ 8,ICU200617,1,92,62,25,41,19.5,0.482,25,0,Negative,99.0,2024-07-26 20:30:41.673970,RandomForestClassifier
43
+ 3,ICU200612,1,124,74,36,0,27.8,0.1,30,1,Negative,69.0,2024-07-26 20:30:58.738631,RandomForestClassifier
44
+ 5,ICU200614,4,183,0,0,0,28.4,0.212,36,1,Positive,52.0,2024-07-26 20:30:58.738631,RandomForestClassifier
45
+ 6,ICU200615,1,124,60,32,0,35.8,0.514,21,1,Negative,79.0,2024-07-26 20:30:58.738631,RandomForestClassifier
46
+ 8,ICU200617,1,92,62,25,41,19.5,0.482,25,0,Negative,99.0,2024-07-26 20:30:58.738631,RandomForestClassifier
47
+ 0,ICU842996-gen-yvxc,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 22:20:43.920677,RandomForestClassifier
48
+ 0,ICU453751-gen-bnlu,10,10,10,10,10,10.0,10.0,0,1,Negative,72.0,2024-07-26 22:21:25.044488,RandomForestClassifier
pages/01_🕰️_History.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from streamlit_extras.dataframe_explorer import dataframe_explorer
3
+
4
+ import asyncio
5
+ from aiocache import cached, Cache
6
+
7
+ import pandas as pd
8
+
9
+ from utils.navigation import navigation
10
+ from utils.footer import footer
11
+
12
+ from config import HISTORY_FILE
13
+
14
+ # Set page configuration
15
+ st.set_page_config(
16
+ page_title='History Page',
17
+ page_icon='🕰️',
18
+ layout="wide",
19
+ initial_sidebar_state='auto'
20
+ )
21
+
22
+
23
+ # @st.cache_data(show_spinner="Getting history of predictions...")
24
+ @cached(ttl=10, cache=Cache.MEMORY, namespace='streamlit_savedataset')
25
+ async def get_history_data():
26
+ try:
27
+ df_history = pd.read_csv(HISTORY_FILE, index_col=0)
28
+ df_history['time_of_prediction'] = [timestamps[0]
29
+ for timestamps in df_history['time_of_prediction'].str.split('.')[0:]]
30
+
31
+ df_history['time_of_prediction'] = pd.to_datetime(
32
+ df_history['time_of_prediction'])
33
+ except Exception as e:
34
+ df_history = None
35
+
36
+ return df_history
37
+
38
+
39
+ async def main():
40
+ st.title("Prediction History 🕰️")
41
+
42
+ # Navigation
43
+ await navigation()
44
+
45
+ df_history = await get_history_data()
46
+
47
+ if df_history is not None:
48
+ df_history_explorer = dataframe_explorer(df_history, case=False)
49
+
50
+ st.dataframe(df_history_explorer)
51
+ else:
52
+ st.info(
53
+ "There is no history file yet. Make a prediction.", icon='ℹ️')
54
+
55
+ # Add footer
56
+ await footer()
57
+
58
+
59
+ if __name__ == "__main__":
60
+ asyncio.run(main())
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ streamlit==1.36.0
2
+ extra-streamlit-components==0.1.71
3
+ streamlit-extras==0.4.3
4
+
5
+ httpx==0.27.0
6
+ aiocache==0.12.2
7
+
8
+ python-dotenv==1.0.1
utils/footer.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+
3
+
4
+ async def footer():
5
+ footer = """
6
+ <style>
7
+ .footer {
8
+ position: fixed;
9
+ bottom: 0;
10
+ left: 0;
11
+ width: 100%;
12
+ background-color: transparent;
13
+ color: #333;
14
+ text-align: center;
15
+ padding: 10px;
16
+ box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.1);
17
+ z-index: 100; /* Prevent overlaying of page content on footer */
18
+ }
19
+ </style>
20
+ <div class="footer">
21
+ &copy; 2024. Made with 💖 <a href="https://www.linkedin.com/in/dr-gabriel-okundaye" target="_blank" style="text-decoration: none;">Gabriel Okundaye</a>
22
+ <span style="color: #aaaaaa;">& Light ✨</span><br>
23
+ </div>
24
+ """
25
+
26
+ return st.markdown(footer, unsafe_allow_html=True)
utils/janitor.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import re
3
+
4
+
5
+ class Janitor:
6
+ def __init__(self):
7
+ pass
8
+
9
+ def clean_dataframe(self, df: pd.DataFrame) -> pd.DataFrame:
10
+ # Apply all cleaning procedure in sequence
11
+ df = df.copy() # First make a copy to preserve integrity of the old df
12
+ df = self.drop_duplicates(df)
13
+ df = self.snake_case_columns(df)
14
+ df = self.fix_none(df)
15
+ df = self.fix_datatypes(df)
16
+ df = self.dropna_target(df)
17
+ df = df.reset_index(drop=True) # Fix index
18
+ return df
19
+
20
+ def drop_duplicates(self, df):
21
+ return df.drop_duplicates() if df.duplicated().sum() > 0 else df
22
+
23
+ def snake_case_columns(self, df):
24
+ pattern = r'(?<!^)(?=[A-Z][a-z])|(?<=[a-z])(?=[A-Z])'
25
+ df.columns = [re.sub(pattern, '_', column).lower()
26
+ for column in df.columns]
27
+ return df
28
+
29
+ def fix_none(self, df):
30
+ def replace_none(value):
31
+ like_nan = {'none', ''}
32
+ if pd.isnull(value) or (isinstance(value, str) and (value.lower().strip() in like_nan)):
33
+ value = pd.NA
34
+ return value
35
+
36
+ return df.map(replace_none)
37
+
38
+ def fix_datatypes(self, df):
39
+ columns_int = ['prg', 'pl', 'pr', 'sk', 'ts', 'age']
40
+ columns_float = ['m11', 'bd2']
41
+ col_to_fix = {col for col in columns_int+columns_float}
42
+ if col_to_fix.issubset(df.columns):
43
+ df[columns_int] = df[columns_int].astype(int)
44
+ df[columns_float] = df[columns_float].astype(float)
45
+ return df
46
+
47
+ # Drop rows with missing values in target column and reset index
48
+ def dropna_target(self, df):
49
+ return df.dropna(subset='sepsis') if 'sepsis' in df.columns else df
utils/navigation.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+
3
+
4
+ async def navigation():
5
+ # Navigation
6
+ st.sidebar.page_link("app.py", label="Home", icon="🤖")
7
+ st.sidebar.page_link("pages/01_🕰️_History.py", label="History", icon="🕰️")
8
+
9
+ # Divider
10
+ st.sidebar.divider()