Background
I was recently tasked with a project where the goal was to envision how to create a tool that could help clinicians identify mortality risks in patients with Covid-19 entering the intensive care unit. This tool, also known as a ‘clinical decision support system,’ is needed to empower clinicians with data-driven health information, such as previous COVID-19 patient outcomes or current patient mortality risk, to make well-informed care decisions.
As any true tinkerer would do, I thought the best way to envision something was to create it myself. And, as a python enthusiast, I figured there would be plenty of tools available to make it possible. Therefore, this tutorial outlines how I created a COVID-19 clinical decision support system with powerful python libraries and provides the resources for you to recreate or reimagine the project yourself.
Outline
I viewed this project through the lens of a machine-learning problem. Therefore, the project was outlined as follows:
- Get data (patients from the intensive care unit who had COVID-19)
- Train and test a machine learning model (deceased vs. survived)
- Deploy a machine learning model to a user interface
Installing Libraries
Essential libraries central to this project include Pandas, Pycaret, Shap, and Streamlit. All of which are used to address specific needs.
- Pandas: data manipulation and analysis
- Pycaret: building low-code machine learning pipelines
- Shap: analysis and visualization of feature impact on model predictions
- Streamlit: python driven app development
- Plotly: high-quality, interactive charts
To install these libraries, open a command line prompt or terminal and enter the following.
pip install pandas
pip install pycaret
pip install shap
pip install streamlit
pip install plotly
Additional libraries in this tutorial are components of Streamlit and were used to improve the user interface/ experience.
pip install streamlit_option_menu
pip install streamlit_shap
pip install streamlit_echarts
Getting Data
One of the major hurdles of this project was acquiring data for model training. Since no open source databases were available from individuals with COVID-19 in intensive care, I thought the best route would be creating my own. To do so, I found a paper addressing a similar problem. The paper below outlines several key features that influenced predictions of mortality in COVID-19 patients. I then loosely reverse engineered these features to generate a proxy dataset.
Using Machine Learning to Predict Mortality for COVID-19 Patients on Day 0 in the ICU
import random as rand
import pandas as pd
import numpy as np
samples = 1000
age_m = 58.0
age_std = 9.0
bun_m = 16.0
bun_std = 6.5
creatine_m = 1.1
creatine = 0.3
inr_m = 1.2
inr_std = 0.1
survived = pd.DataFrame()
survived['Age'] = np.random.normal(loc=age_m,scale=age_std,size=samples)
survived['Gender'] = np.random.binomial(1, 0.367, size=samples)
survived['Blood Urea Nitrogen'] = np.random.normal(loc=bun_m,scale=bun_std,size=samples)
survived['Cardiovascular History'] = np.random.binomial(1, 0.291, size=samples)
survived['Neurological History'] = np.random.binomial(1, 0.16, size=samples)
survived['Int Norm Ratio'] = np.random.normal(loc=inr_m,scale=inr_std,size=samples)
age_m = 72.5
age_std = 8.25
bun_m = 30.0
bun_std = 9.0
creatine_m = 1.5
creatine = 0.4
inr_m = 1.3
inr_std = 0.1
deceased = pd.DataFrame()
deceased['Age'] = np.random.normal(loc=age_m,scale=age_std,size=samples)
deceased['Gender'] = np.random.binomial(1, 0.646, size=samples)
deceased['Blood Urea Nitrogen'] = np.random.normal(loc=bun_m,scale=bun_std,size=samples)
deceased['Cardiovascular History'] = np.random.binomial(1, 0.709, size=samples)
deceased['Neurological History'] = np.random.binomial(1, 0.840, size=samples)
deceased['Int Norm Ratio'] = np.random.normal(loc=inr_m,scale=inr_std,size=samples)
Then, combined, shuffled, and saved the dataframe to represent my model training dataset.
train = pd.concat([survived, deceased])
train = train.sample(frac=1).reset_index(drop=True)
train.to_csv('COVID_TRAIN.csv')
Creating Model
Next, I used Pycaret’s supervised classification functions to setup, train, and evaluate a model identifying deceased versus survived.
Creating UI
Once the model was suitable for my needs, I began creating the user interface. The three main components I felt necessary for clinicians were:
- Ease of use
- Interpretable model predictions
- Feature exploration of current and past patients
Streamlit offers the capability to use strictly python-syntax to build beautiful web applications. Thus, the user interface is built solely with Streamlit and related components.
Setup
Install libraries and set Streamlit page configurations.
import streamlit as st
import pandas as pd
from streamlit_option_menu import option_menu
from pycaret.classification import *
import plotly.express as px
import shap
from streamlit_shap import st_shap
from streamlit_echarts import st_echarts
#set settings for streamlit page
st.set_page_config(layout="wide",
page_title="COVID Triage",
page_icon="chart_with_upwards_trend")
#hide streamlit menu bar
hide_streamlit_style = """
<style>
#MainMenu {visibility: hidden;}
footer {visibility: hidden;}
</style>
"""
st.markdown(hide_streamlit_style, unsafe_allow_html=True)
Model
Initialize training data and model.
#use pandas to read covid data for model training and creation
train = pd.read_csv('COVID_TRAIN.csv')
#use pycaret to preprocess and train a decision tree supervised ML model
exp = setup(train, target = 'class', silent=True, verbose=False)
dt = create_model('dt')
Menu Bar
Create a menu bar to switch between login and COVID triage.
#option menu from streamlit component streamlit_option_menu
with st.sidebar:
selected = option_menu(None, ["Log In", "COVID Triage"],
icons=['house', "list-task"],
menu_icon="cast", default_index=0, orientation="vertical")
Login Page
Create logic for simple non-functioning login page.
if selected == 'Log In':
st.title('Covid 19 Mortality Risk Clinical Decision Support System')
if 'user_name' not in st.session_state:
st.session_state.user_name = 'User Name'
user_name = st.text_input("User Name", value = st.session_state.user_name)
st.session_state.user_name = user_name
password = st.text_input("Password", type="password")
st.session_state.password = password
if st.session_state.password:
sc = "Welcome " + st.session_state.user_name + " please proceed to COVID Triage"
st.success(sc)
COVID-19 Triage Page – User Inputs
Create widgets to take user input corresponding to features in the training data.
#covid triage page
#feature inputs correspond to training data
if selected == 'COVID Triage':
col1, col2, col3= st.columns(3)
with col1:
name = st.text_input("Patient ID")
with col2:
gender = st.selectbox("Gender", (0, 1), help = "0 = Female, 1 = Male")
with col3:
age = st.number_input("Age", step = 1)
ccol1, ccol2, ccol3, ccol4= st.columns(4)
with ccol1:
bun = st.slider("Blood Urea Nitrogen", min_value=0, max_value=60)
with ccol2:
inr = st.slider("International Normalized Ratio", min_value=0.88, max_value=1.66)
with ccol3:
honeuro = st.selectbox("History of Neurological Disorder", (0, 1), help = "0 = No history of neurological disorders, 1 = History of neurological disorders")
with ccol4:
hocard = st.selectbox("History of Cardiovascular Disorder", (0, 1), help = "0 = No history of cardiovascular disorders, 1 = History of cardiovascular disorders")
test = pd.DataFrame({"Age": [age], "Gender":[int(gender)],"Blood Urea Nitrogen":[bun], "Cardiovascular History":[int(hocard)], "Neurological History":[int(honeuro)], "Int Norm Ratio":[inr]})
COVID-19 Triage Page -Model Prediction and Confidence
Setup the display and logic for prediction and confidence values.
preds = predict_model(dt, test)
st.sidebar.text('Risk Prediction and Confidence')
preds['Mortality Risk'] = preds['Label'].replace([0,1], ['Low Mortality Risk', 'High Mortality Risk'])
if preds['Label'].iloc[0] == 0:
#display if label = 0 (low mortality risk)
st.sidebar.info(preds['Mortality Risk'].iloc[0])
liquidfill_option = {
"series": [{"type": "liquidFill", "data": [preds['Score'].iloc[0]]}]
}
if preds['Label'].iloc[0] == 1:
#display if label = 1 (high mortality risk)
st.sidebar.error(preds['Mortality Risk'].iloc[0])
liquidfill_option = {
"series": [{"type": "liquidFill", "data": [preds['Score'].iloc[0]], 'color': ['#ff0000']}]
}
with st.sidebar:
#liquid fill chart with confidence value and color corresponding to mortality risk (high = red, low = blue)
st_echarts(liquidfill_option)
#shapley additive explanation of feature weights on model prediction
explainer = shap.KernelExplainer(model = dt.predict_proba, data = get_config('X_train'), link = "identity")
shap_value_single = explainer.shap_values(X = test)
st_shap(shap.force_plot(base_value = explainer.expected_value[0],
shap_values = shap_value_single[0], features=test
))
COVID-19 Triage Page – Patient Graphs
Setup logic to display current patient relationships with previous patients and features.
st.text("Previous COVID-19 ICU Patients")
df = train.copy()
for cols in df.drop(['Unnamed: 0', 'class'], axis=1).columns:
df['Mortality Outcome'] = train['class'].replace([1,0], ['Deceased', 'Survived'])
fig = px.histogram(df, x = cols, color = 'Mortality Outcome',
color_discrete_map = {'Deceased':'red','Survived':'blue'})
fig.add_vline(x=test[cols].iloc[0], line_dash="dot",
annotation_text="Current Patient",
annotation_position="top left",
annotation_font_size=10,
annotation_font_color="gray"
)
st.plotly_chart(fig, config= dict(
displayModeBar = False))
Putting It All Together
The user interface was ready to be deployed. The final steps included saving the requirements, adding all files to GitHub, and deploying to the Streamlit sharing service. To access the final product, visit the link below or build it yourself on your local machine from my covidtriageStreamlit repository.
https://covidtriage.streamlitapp.com/
GitHub – chags1313/covidtriageStreamlit: COVID triage application built with streamlit
Conclusion
This tutorial provided an in-depth look at how to use python’s great ecosystem of tools to make a useful product. In this case, we built a clinical decision support system to identify mortality risk in COVID-19 patients entering intensive care using Pandas, Pycaret, Shap, Plotly, and Streamlit.
Thank you for reading! Check out my GitHub for more code and resources or reach out to me on LinkedIn with any questions or comments.
https://www.linkedin.com/in/cole-hagen/
Related Articles from Author
Can you Teach a Computer to See Your Pain?