Techno Blender
Digitally Yours.

COVID-19 Mortality Triage with Streamlit, Pycaret, and Shap | by Cole Hagen | Oct, 2022

0 59


Building a Clinical Decision Support System with Powerful Python Tools

Image from Patrick Assale @ Unsplash

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.

Sneak Peak at the Final Product: https://covidtriage.streamlitapp.com/

Outline

I viewed this project through the lens of a machine-learning problem. Therefore, the project was outlined as follows:

  1. Get data (patients from the intensive care unit who had COVID-19)
  2. Train and test a machine learning model (deceased vs. survived)
  3. 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

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.

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')

Next, I used Pycaret’s supervised classification functions to setup, train, and evaluate a model identifying deceased versus survived.

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/


Building a Clinical Decision Support System with Powerful Python Tools

Image from Patrick Assale @ Unsplash

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.

Sneak Peak at the Final Product: https://covidtriage.streamlitapp.com/

Outline

I viewed this project through the lens of a machine-learning problem. Therefore, the project was outlined as follows:

  1. Get data (patients from the intensive care unit who had COVID-19)
  2. Train and test a machine learning model (deceased vs. survived)
  3. 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

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.

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')

Next, I used Pycaret’s supervised classification functions to setup, train, and evaluate a model identifying deceased versus survived.

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/

FOLLOW US ON GOOGLE NEWS

Read original article here

Denial of responsibility! Techno Blender is an automatic aggregator of the all world’s media. In each content, the hyperlink to the primary source is specified. All trademarks belong to their rightful owners, all materials to their authors. If you are the owner of the content and do not want us to publish your materials, please contact us by email – [email protected]. The content will be deleted within 24 hours.

Leave a comment