markov / app.py
arinbjorn's picture
V1
8860bd4
import streamlit as st
import altair as alt
import numpy as np
import pandas as pd
st.markdown(
"""
<style>
@font-face {
font-family: 'Tangerine';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/tangerine/v12/IurY6Y5j_oScZZow4VOxCZZM.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
html, body, [class*="css"] {
font-family: 'Public Sans', sans-serif;
# font-size: 1rem;
}
</style>
""",
unsafe_allow_html=True,
)
# Define params
st.subheader("Configuration")
col1, col2 = st.columns(2)
# Chances of developing symptoms (per day)
with col1:
symptoms_chance = st.slider(
'Chances of developing symptoms if infected (per day)', min_value=0.0, max_value=1.0, value=0.5, step=0.01)
# Days spent inf asympt
with col1:
mean_days_inf_asympt = st.slider(
'Mean number of days as infectious asymptomatic (without routine testing)', min_value=1, max_value=14, value=4, step=1)
base_p00 = 1-(1/mean_days_inf_asympt)
base_p01 = (1-symptoms_chance)*(1/mean_days_inf_asympt)
base_p03 = (symptoms_chance)*(1/mean_days_inf_asympt)
# Days spent inf asympt
with col2:
mean_days_inf_sympt = st.slider(
'Mean number of days as infectious symptomatic (when testing on symptoms only)', min_value=1, max_value=14, value=2, step=1)
base_p11 = 1-(1/mean_days_inf_sympt)
base_p12 = (1/mean_days_inf_sympt)
# Wearable efficiency
efficiency = st.radio(
"Performance of device",
('Standard', 'Conservative'))
# with col2:
# wear_efficiency = st.slider(
# 'Sensitivity of device', min_value=0.0, max_value=1.0, value=0.2, step=0.01) # 👈 this is a widget
# # Calculate
# test_efficiency = np.linspace(1, 30, 30)
# days_inf = np.zeros((len(test_efficiency)))
# temp_df = []
# for tau_count, t_e in enumerate(test_efficiency):
# tau = 1/t_e
# pi = wear_efficiency
# # Transition matrix
# p = np.array([
# [base_p00*(1-tau)*(1-pi), base_p01*(1-tau) *
# (1-pi), 1-(1-tau)*(1-pi), base_p03*(1-tau)*(1-pi)],
# [0, base_p11*(1-tau)*(1-pi), base_p12*(1+tau+pi-tau*pi), 0.0],
# [0, 0, 1.0, 0.0],
# [0, 0, 0.0, 1.0]
# ])
# m1 = 1/(1-p[0,0])
# m2 = 1/(1-p[1,1])
# p2 = p[0,1]/(p[0,1]+p[0,2]+p[0,3])
# days_inf[int(tau_count)] = m1 + p2*m2
# routine_tests_required = 30 * days_inf[2]
# Cost case
sens_list_standard = {0.0: 0.0,
0.005: 0.05,
0.014: 0.1,
0.021: 0.15,
0.05: 0.295,
0.1: 0.434,
0.2: 0.6,
0.3: 0.72,
0.4: 0.79,
0.5: 0.86,
0.6: 0.9,
0.7: 0.925,
0.8: 0.97,
0.9: 0.99,
1.0: 1.0}
sens_list_conservative = {
0: 0,
0.012: 0.050,
0.026: 0.105,
0.049: 0.149,
0.072: 0.198,
0.096: 0.248,
0.120: 0.297,
0.146: 0.347,
0.184: 0.396,
0.222: 0.446,
0.255: 0.495,
0.300: 0.545,
0.349: 0.594,
0.401: 0.644,
0.467: 0.693,
0.547: 0.743,
0.621: 0.792,
0.699: 0.842,
0.787: 0.891,
0.868: 0.941,
1: 1
}
if efficiency == 'Standard':
sens_list = sens_list_standard
else:
sens_list = sens_list_conservative
def roc_func(x):
return sens_list[x]
def roc_random(x):
return x
test_efficiency = np.array([7, 30, 10000])
# FPR = np.linspace(0, 1, 11)
# FPR = [0.0, 0.005, 0.016, 0.021, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
# FPR = list(sens_list.keys())
# days_inf = np.zeros((len(test_efficiency), len(FPR)))
# for tau_count, t_e in enumerate(test_efficiency):
# tau = 1/t_e
# for fi_count, fi in enumerate(FPR):
# pi = roc_func(fi)
# alpha = tau + pi - (tau*pi)
# m1 = 4/(1+3*alpha)
# m2 = 2/(1 + alpha)
# p2 = 1/2 * (1 - alpha) / (1 + 3 * alpha)
# days_inf[int(tau_count), int(fi_count)] = m1 + p2*m2
# Calculate
test_efficiency = np.array([7, 30, 10000])
FPR = list(sens_list.keys())
days_inf = np.zeros((len(test_efficiency), len(FPR)))
temp_df = []
for tau_count, t_e in enumerate(test_efficiency):
tau = 1/t_e
for fi_count, fi in enumerate(FPR):
pi = roc_func(fi)
# Transition matrix
p = np.array([
[base_p00*(1-tau)*(1-pi), base_p01*(1-tau) *
(1-pi), 1-(1-tau)*(1-pi), base_p03*(1-tau)*(1-pi)],
[0, base_p11*(1-tau)*(1-pi), base_p12*(1+tau+pi-tau*pi), 0.0],
[0, 0, 1.0, 0.0],
[0, 0, 0.0, 1.0]
])
m1 = 1/(1-p[0, 0])
m2 = 1/(1-p[1, 1])
p2 = p[0, 1]/(p[0, 1]+p[0, 2]+p[0, 3])
days_inf[int(tau_count), int(fi_count)] = m1 + p2*m2
routine_tests_required = 30 * days_inf[2]
# print(routine_tests_required)
# No wearable case
no_wearables = []
tau = 1/10000
for fi_count, fi in enumerate(FPR):
pi = roc_random(fi)
# Transition matrix
p = np.array([
[base_p00*(1-tau)*(1-pi), base_p01*(1-tau) *
(1-pi), 1-(1-tau)*(1-pi), base_p03*(1-tau)*(1-pi)],
[0, base_p11*(1-tau)*(1-pi), base_p12*(1+tau+pi-tau*pi), 0.0],
[0, 0, 1.0, 0.0],
[0, 0, 0.0, 1.0]
])
m1 = 1/(1-p[0, 0])
m2 = 1/(1-p[1, 1])
p2 = p[0, 1]/(p[0, 1]+p[0, 2]+p[0, 3])
no_wearables.append(m1 + p2*m2)
cost = np.array(FPR)*30
no_wearable_cost = cost
# for i in range(len(test_efficiency)):0
wearable_cost = (1-(1-np.array(FPR))*(1-1/test_efficiency[2]))*30
wearable_days_inf = days_inf[2]
# Create chart
chart_data = pd.DataFrame(
{'Tests required per month': no_wearable_cost,
'Routine testing': no_wearables,
'Wearable-triggered testing': wearable_days_inf})
# st.line_chart(chart_data)
chart_data_melted = chart_data.melt('Tests required per month')
print(chart_data_melted)
chart = (
alt.Chart(
data=chart_data_melted,
title="",
height=400,
)
.mark_line()
# .encode(
# x=alt.X('Tests required per month',
# scale=alt.Scale(domain=[0, 30])),
# y=alt.Y('Average case infectious days',
# scale=alt.Scale(domain=[0, 6])),
# # color=alt.value("#162d88"),
# color=alt.Color("name:N"),
# strokeWidth=alt.value(6),
# )
.encode(
x='Tests required per month',
y=alt.Y('value:Q', axis=alt.Axis(title='Average case infectious days')),
# y='value:Q',
color='variable:N',
strokeWidth=alt.value(6)
)
.configure_axis(
labelFontSize=20,
titleFontSize=20
)
)
st.subheader("Outcome")
st.altair_chart(chart, use_container_width=True)
# col1, col2, col3 = st.columns(3)
# col1.metric("Tests required per month", int(routine_tests_required), "1.2")
# col2.metric("Tests saved", "9", "-8%")
# col3.metric("Humidity", "86%", "4%")