Link

from scipy.stats import beta import matplotlib.pyplot as plt import math import numpy as np import random import seaborn as sns import pandas as pd

sns.set()

master_dict = {} master_dict = {‘test_period’: ‘Test Period’,’test_S’: ‘Susceptible Population’, ‘test_E’: ‘Exposed Population’, ‘test_NE’: ‘Newly Exposed’, ‘test_I’: ‘Infectious Population’, ‘test_NI’: ‘Newly infectious’, ‘test_NI_C’: ‘Total infectious’, ‘test_IC’: ‘Daily confirmed infections’, ‘test_IC_H’: ‘Daily hospitalized’, ‘test_IC_No_H’: ‘Daily Confirmed, Not Hospitalized’, ‘test_IU’: ‘Daily Uncomfirmed Infected’, ‘test_IC_C’: ‘Cumulative confirmed infections’, ‘test_R’: ‘Recovered Population’, ‘test_NR’: ‘Newly Recovered’, ‘test_D’: ‘Dead Population’, ‘test_ND’: ‘Newly Dead’, ‘test_D_C’: ‘Cumulative Dead’}

#conf_perc = 444 / 10_000_000 #print(conf_perc) print(master_dict.get(‘test_I’))

N = 10_000 # population size S = 0 # susceptible E = 0 # exposed I = 0 # infectious H = C_H = 0 # hospitalizations, confirmed and hospitalized C = 250 # confirmed case C_NH = 0 # confirmed not in hospital U = 0 # unconfirmed case R = 0 # recovering D = 0 # dead S_R = 0 # recovered but susceptible again

C = H + C_NH I = C + U

h2i = .0045 # H1N1 ratio for hospitalized to infected h2i_max = .012 h2i_min = .0016

c2i = 1 / 79 # H1N1 ratio for confirmed cases to estimated total infected c2i_min = 1 / 148 c2i_max = 1 / 47

person-to-person spread: primary means of propagation https://www.cdc.gov/coronavirus/2019-ncov/prepare/transmission.html?CDC_AA_refVal=https%3A%2F%2Fwww.cdc.gov%2Fcoronavirus%2F2019-ncov%2Fabout%2Ftransmission.html

rep_contact = 4 rep_contact_std = 2 new_contact = 2 new_contact_std = 1

E_dt_mean = 5.1 # length of incubation period E_dt_max = 11.5 # max CI value of 15.6 E_dt_min = 2.5 # min CI value of 1.9

i_mild = .51 # mild infection rate i_mod = .388 # mod inf rate i_severe = .052 # severe inf rate i_critical = .006 # critical rate i_asym = .044 # asymptomatic rate

spread mostly likely during infectious period, unlikely once recovery begins: https://www.statnews.com/2020/03/09/people-shed-high-levels-of-coronavirus-study-finds-but-most-are-likely-not-infectious-after-recovery-begins/

i_rec_mean = 7 # infectious period, time to begining recovery after exposure to infectious transition i_rec_max = 12 i_rec_min = 4

f_rate_mean = .015 # (.009 default) case fatality rate, https://wwwnc.cdc.gov/eid/article/26/6/20-0320_article f_rate_min = .0025 # max CFR (.0025) f_rate_max = .03 # max CFR (.03 default)

C = 395 quar_num = 500 # number of confirmed cases to trigger quarantine

def dangerPerception(dp_c, dp_q): #print(dp_c/dp_q) if (dp_c/dp_q) <= 0.05: return .05 elif (dp_c/dp_q) <= 0.95: return dp_c / dp_q else: return 0.95

#dang_perc = dangerPerception(C, quar_num) #print(dang_perc)

def behaviorModifier(bm_dp): beta_a = 10 - (10 * bm_dp) beta_b = 10 - beta_a #print(beta_a, beta_b) return np.random.beta(beta_a, beta_b)

#beh_mod = behaviorModifier(dang_perc) #print(beh_mod)

def calc_stdv(cs_mean, cs_min, cs_max): cs_avg = ((cs_mean - cs_min) + (cs_max - cs_mean)) / 2 return cs_avg / 1.65

i_rec_stdv = calc_stdv(i_rec_mean, i_rec_min, i_rec_max) #print(i_rec_stdv)

e_dt_stdv = calc_stdv(E_dt_mean, E_dt_min, E_dt_max) #print(e_dt_stdv)

def offset_normal(m, a, b, r): a_sd = (m - a) / 1.69 b_sd = (b - m) / 1.69 o_n = [] for x in range(r): i = np.random.uniform(a, b) if i < m: av = np.random.normal(m, a_sd) if av > m: av = m - (av - m) if av < 0: av = 0 o_n.append(av) if i >= m: bv = np.random.normal(m, b_sd) if bv < m: bv = m + (m - bv) o_n.append(bv) return o_n

def norm_norm(m, s, r): n_n = [] for x in range(r): i = np.random.normal(m, s) if i < 0: i = 0 n_n.append(i) return n_n

infectious_period_offset = offset_normal(i_rec_mean, i_rec_min, i_rec_max, N)

plt.hist(infectious_period_offset, 50)

plt.show()

incubation_period_offset = offset_normal(E_dt_mean, E_dt_min, E_dt_max, N)

plt.hist(incubation_period_offset, 50)

plt.show()

h2i_offset = offset_normal(h2i, h2i_min, h2i_max, N)

plt.hist(h2i_offset, 50)

plt.show()

r_contact_norm = norm_norm(rep_contact, rep_contact_std, N)

plt.hist(r_contact_norm, 50)

plt.show()

n_contact_norm = norm_norm(new_contact, new_contact_std, N)

plt.hist(n_contact_norm, 50)

plt.show()

c2i_offset = offset_normal(c2i, c2i_min, c2i_max, N)

plt.hist(c2i_offset, 50)

plt.show()

f_rate_offset = offset_normal(f_rate_mean, f_rate_min, f_rate_max, N)

Running the main ensemble test here:

quar_num_test = 10 big_S = []

for num in range(1_000):

test_period = 180 # days in test period
test_S = [N] # total susceptible
test_E = [1] # total exposed
test_NE = [0] # new exposed
test_I = [0] # total infectious
test_NI = [0] # new infectious
test_NI_C = [0] # total infectious
test_IC = [0] # confirmed infections
test_IC_H, test_IC_No_H, test_IU = [0], [0], [0]
test_IC_C = [0] # cumulative confirmed infections
test_R = [0] # total recovered
test_NR = [0] # new recovered
test_D = [0] # total dead
test_ND = [0] # new dead
test_D_C = [0] # cumulative dead

inf_per = random.sample(infectious_period_offset, 1)[0]
if inf_per < 1:
    inf_per = 1
inc_per = random.sample(incubation_period_offset, 1)[0]
if inc_per < 1:
    inc_per = 1
h2i_test = random.sample(h2i_offset, 1)[0]
c2i_test = random.sample(c2i_offset, 1)[0]
r_cont_test = random.sample(r_contact_norm, 1)[0]
n_cont_test = random.sample(n_contact_norm, 1)[0]
f_rate_test = random.sample(f_rate_offset, 1)[0]

rec_rate = 1 / inf_per # recovery rate, conversion rate from infectious to recovering
lat_rate = 1 / inc_per # latent rate, conversion rate from exposed to infectious
f_rate_rate = f_rate_test / inf_per # case fatality rate *per day* of infectiousness

for day in range(test_period):
    
    today = day + 1
    yesterday = day

    cur_sus = test_S[yesterday] # today's number of currently susceptible - the population that has not been exposed yet
    confirmed_cases = test_IC_C[yesterday] # confirmed cases based on the the H1N1 study proportions of infectious to confirmed

    dang_per_test = dangerPerception(confirmed_cases, quar_num_test) # testing for danger perception given the number of confirmed cases and max risk or quarantine number
    beh_mod_test = behaviorModifier(dang_per_test) # behavioral modification multiplier given the danger/risk perception

    new_NE = (beh_mod_test * r_cont_test * test_NI[yesterday]) + (beh_mod_test * n_cont_test * test_NI[yesterday]) # personal/close contacts and exposures for the newly infectious
    
    not_new_I = test_I[yesterday] - test_NI[yesterday] - test_IC_H[yesterday]
    if not_new_I < 0:
        not_new_I = 0
    not_new_NE = beh_mod_test * n_cont_test * not_new_I # continued personal/close contacts with non-repeat persons for the infectious at date of infectious > 1
    
    next_NE = new_NE + not_new_NE # new exposed population today
    if next_NE >= cur_sus:
        next_NE = test_S[yesterday]

    next_NI = test_E[yesterday] * lat_rate # new infectious population today
    next_NI_C = test_NI_C[yesterday] + next_NI # cumulative infectous population

    next_E = test_E[yesterday] + next_NE - next_NI # total exposed -> adds new exposures, loses new infectious
    if next_E < 0:
        next_E = 0
    
    next_IC = c2i_test * test_I[yesterday] # current confirmed infectious based on H1N1 ratios and estimated total infectious
    new_IC = next_IC - test_IC[yesterday]
    if new_IC < 0:
        new_IC = 0

    next_IC_C = test_IC_C[yesterday] + new_IC # cumulative confirmed infectious
    
    next_IC_H = h2i_test * test_I[yesterday] # current hospitalized infectious based on H1N1 ratios and estimated total infectious

    next_NR = test_I[yesterday] * rec_rate # new recoveries today
    next_R = test_R[yesterday] + next_NR # cumulative recovery rate

    next_D = test_IC[yesterday] * f_rate_rate
    next_ND = next_D - test_D[yesterday]
    if next_ND < 0:
        next_ND = 0

    next_D_C = test_D_C[yesterday] + next_D

    next_I = test_I[yesterday] + next_NI - next_NR - next_ND
    if next_I < 0:
        next_I = 0
    
    next_S = N - next_E - next_I - next_R - next_D
    if next_S < 0:
        next_S = 0

    test_S.append(next_S)
    test_E.append(next_E)
    test_NI.append(next_NI)
    test_NI_C.append(next_NI_C)
    test_I.append(next_I)
    test_NR.append(next_NR)
    test_R.append(next_R)
    test_ND.append(next_ND)
    test_D.append(next_D)
    test_D_C.append(next_D_C)
    test_IC.append(next_IC)
    test_IC_H.append(next_IC_H)
    test_IC_C.append(next_IC_C)

# if len(big_S) > 0:
#     big_S = np.vstack((big_S, test_IC))
# else:
#     big_S = test_IC

test_num = num
test_name = "Test #" + str(num)
test_1 = test_I
test_2 = test_D
try: df
except NameError: df = None
if df is None:
    df = pd.DataFrame({test_name: test_1})
else:
    df_new = pd.DataFrame({test_name: test_1})
    df = pd.concat([df, df_new], axis=1)

try: df2
except NameError: df2 = None
if df2 is None:
    df2 = pd.DataFrame({test_name: test_2})
else:
    df_new2 = pd.DataFrame({test_name: test_2})
    df2 = pd.concat([df2, df_new2], axis=1)

big_array = np.stack([test_S, test_E, test_I, test_IC, test_IC_H, test_R, test_D]).T

plt.plot(big_array)

plt.legend([‘Sus’, ‘Exp’, ‘Inf’, ‘Conf’, ‘Hosp’, ‘Rec’, ‘Dead’])

plt.xlabel(‘Days’)

plt.show()

lombard_conf_new = [0, 0, 15, 40, 57, 61,67,65,98,128,84,369,270,266,300,431,361,808,769,1280,322,1489,1445,1095,1865,1587,1377,1571,1493,2171, 2380, 3251, 1691, 1555, 1942, 1643, 2543, 2409] hubei_conf = [0, 0, 0, 0, 0, 444,444,549,761,1058,1423,3554,3554,4903,5806,7153,11177,13522,16678,19665,22112,24953,27100,29631,31728,33366,33366,48206,54406,56249,58182,59989,61682,62031,62442,62662,64084,64084,64287,64786,65187,65596,65914,66337,66907,67103,67217,67332,67466,67592,67666,67707,67743,67760,67773,67781,67786,67790,67794,67798,67799,67800,67800] ny_conf = [1, 1,2,2,4,5,12,14,20,37,52,96,155,269,330,464,645,1339,2468,4408,6211,9045,12305,14905,20011,23112,25399]

hubei_fat = [0, 0, 0, 0, 0, 0, 0, 0, 0, 17,17,24,40,52,76,125,125,162,204,249,350,414,479,549,618,699,780,871,974,1068,1068,1310,1457,1596,1696,1789,1921,2029,2144,2144,2346,2346,2495,2563,2615,2641,2682,2727,2761,2803,2835,2871,2902,2931,2959,2986,3008,3024,3046,3056,3062,3075,3085,3099,3111,3122,3130] lombard_fat = [0, 0, 0, 1, 1, 4, 2, 1, 5, 3, 6, 1, 14, 17, 18, 25, 37, 19, 113, 66, 135, 149, 127, 146, 76, 252, 202, 220, 319, 209, 381, 546, 361, 320, 402, 296, 387, 541] ny_deaths = [0, 0, 0,0,0,0,0,0,0,0,0,0,0,0,1,5,7,10,20,22,43,60,99,131,192,280,365,450]

lombard_conf = np.cumsum(lombard_conf_new) # Lombardi region only (pop 10m) - population density probably doesn’t work for calc entire countries lombard_cum = np.cumsum(lombard_fat) italy_norm = [i/1000 for i in lombard_conf] # Lombardi region only hubei_norm = [i/1100 for i in hubei_conf] ny_norm = [i/1880 for i in ny_conf]

hubei_fat_norm = [i/1100 for i in hubei_fat] lombard_fat_norm = [i/1000 for i in lombard_cum] ny_deaths_norm = [i/1880 for i in ny_deaths]

print(hubei_fat_norm)

df.plot(alpha=0.3, legend=False).set(title = f’COVID-19 per 10k Population - 1k Model Runs’, xlabel=’Day’, ylabel=’Per 10k Population’) plt.show()

df_mean = df.mean(axis = 1) df_10 = df.quantile(.1, axis=1) df_90 = df.quantile(.9, axis=1)

df2_mean = df2.mean(axis = 1)

df_mean.plot() df_10.plot() df_90.plot() plt.show()

sns.lineplot(data=df_mean, label=’Model 1’).set(title = ‘COVID-19 Models per 10,000 Simulated’, xlabel = ‘Day’, ylabel = ‘Per 10,000’) df_90.plot(label=’90 percentile’) df_10.plot(label=’10 percentile’)

plt.plot(hubei_norm, label=’Hubei’)

plt.plot(lombard_fat_norm, label=’Lombardy (ITA)’)

plt.plot(ny_deaths_norm, label=’NYC Confirmed Cases’) #sns.lineplot(data=df2_mean, label=’Model 2’)

plt.plot(ny_norm, label=’New York’)

plt.plot(italy_norm, label=’Lombardi (ITA)’)

legend = plt.legend()

legend.get_texts()[0].set_text(‘Hubei’)

plt.legend() plt.show()

plt.plot(big_S_stack.T)

plt.show()

df_mean.to_csv(‘test_df_1.csv’)