| | 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, |
| | ) |
| |
|
| |
|
| | |
| | st.subheader("Configuration") |
| | col1, col2 = st.columns(2) |
| |
|
| | |
| | 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) |
| |
|
| | |
| | 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) |
| |
|
| | |
| | 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) |
| |
|
| | |
| | efficiency = st.radio( |
| | "Performance of device", |
| | ('Standard', 'Conservative')) |
| | |
| | |
| | |
| |
|
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | 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]) |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | 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) |
| | |
| | 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] |
| | |
| |
|
| | |
| | no_wearables = [] |
| | tau = 1/10000 |
| | for fi_count, fi in enumerate(FPR): |
| | pi = roc_random(fi) |
| | |
| | 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 |
| | |
| |
|
| | wearable_cost = (1-(1-np.array(FPR))*(1-1/test_efficiency[2]))*30 |
| | wearable_days_inf = days_inf[2] |
| |
|
| | |
| | chart_data = pd.DataFrame( |
| | {'Tests required per month': no_wearable_cost, |
| | 'Routine testing': no_wearables, |
| | 'Wearable-triggered testing': wearable_days_inf}) |
| |
|
| | |
| |
|
| | 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='Tests required per month', |
| | y=alt.Y('value:Q', axis=alt.Axis(title='Average case infectious days')), |
| | |
| | color='variable:N', |
| | strokeWidth=alt.value(6) |
| | ) |
| | .configure_axis( |
| | labelFontSize=20, |
| | titleFontSize=20 |
| | ) |
| |
|
| | ) |
| |
|
| | st.subheader("Outcome") |
| | st.altair_chart(chart, use_container_width=True) |
| |
|
| | |
| | |
| | |
| | |
| |
|