Skip to main content

Stochastic simulation

Stochastic SIR

def SIR_model_stochastic(S_initial=1000, I_initial=1, R_initial=0, n_steps=1000, beta=0.4, gamma=0.4):
"""
Simulate SIR model.

Parameters
----------
S_initial : int
Initial number of susceptible individuals.
I_initial : int
Initial number of infected individuals.
R_initial : int
Initial number of recovered individuals.
n_steps : int
Number of steps to simulate.
beta : float
Infection rate (probability of infection on contact)
gamma : float
Recovery rate (probability of recovery per unit time)

Returns
-------
S_dict : dict
Dictionary with number of susceptible individuals at each time step.
{t: S_t, ... }
I_dict : dict
Dictionary with number of infected individuals at each time step.
{t: I_t, ... }
R_dict : dict
Dictionary with number of recovered individuals at each time step.
{t: R_t, ... }
df_data : list
List of dictionary recording number of S, I, R at each timestep. This can be converted into a Pandas dataframe by pd.DataFrame(df_data).
[ {'t': t, 'S': S_new, 'I': I_new, 'R': R_new}, ... ]
"""

# Set S,I,R to initial values
S = S_initial
I = I_initial
R = R_initial

# N is always constant
N = S+I+R

# Initialize dictionaries to store values
# {t: S_t, ... } etc.
# t=0 is the initial state
S_dict = {0: S}
I_dict = {0: I}
R_dict = {0: R}
df_data = [{'t': 0, 'S': S, 'I': I, 'R': R, 'lambda': beta*(I/N)}]

# Start simulation from t=1 to t=n_steps
for t in range(1, n_steps+1):

# Calculate number of individuals for each compartment at next time step
lambda_t = beta*(I/N)
# lambda_t = beta*(I)

# S_diff = np.random.poisson(lambda_t)
S_diff = np.random.binomial(S, lambda_t)
I_diff = np.random.binomial(I, gamma)

S_new = S - S_diff
I_new = I + S_diff - I_diff
R_new = R + I_diff

# Store values
S_dict[t] = S_new
I_dict[t] = I_new
R_dict[t] = R_new

# Data for pandas
df_data.append({'t': t, 'S': S_new, 'I': I_new, 'R': R_new, 'lambda': lambda_t})

# Update values for next time step
S = S_new
I = I_new
R = R_new

return S_dict, I_dict, R_dict, df_data
It_2darray_sim = []
sim_data = []

for it in trange(n_sim):

# Run simulation
_, I, _, df_list = SIR_model_stochastic(
S_initial=S_initial,
I_initial=I_initial,
R_initial=0,
n_steps=n_steps,
beta=beta,
gamma=mu
)
It_2darray_sim.append(list(I.values()))

# For dataframe
for item in df_list:
item['iter'] = it
sim_data.extend(df_list)

It_2darray_sim = np.array(It_2darray_sim).transpose()
100%|██████████| 10000/10000 [00:02<00:00, 3371.06it/s]
# Convert data to dataframe for summarization
df_sim = pd.DataFrame(sim_data)
def q25(x):
return x.quantile(0.25)

def q75(x):
return x.quantile(0.75)
It_summary = df_sim.groupby('t')['I'].agg(['median', 'mean', q25, q75])
It_summary
median mean q25 q75
t
0 20.0 20.0000 20.0 20.0
1 22.0 22.0017 20.0 24.0
2 24.0 24.2170 22.0 27.0
3 26.0 26.6029 23.0 30.0
4 29.0 29.2637 25.0 33.0
... ... ... ... ...
196 1.0 1.1972 0.0 2.0
197 0.0 1.1243 0.0 2.0
198 0.0 1.0563 0.0 2.0
199 0.0 0.9973 0.0 2.0
200 0.0 0.9395 0.0 1.0

201 rows × 4 columns

lambda_summary = df_sim.groupby('t')['lambda'].agg(['median', 'mean', q25, q75])
# S_summary = df_sim.groupby('t')['S'].agg(['median', 'mean', q25, q75])
St_summary = df_sim.groupby('t')['S'].agg(['median', 'mean', q25, q75])
fig, ax = plt.subplots(1,1, figsize=(4,3))

# ax.plot(St_summary.index, St_summary['median'], label="S (Median)")
# ax.fill_between(St_summary.index, St_summary['q25'], St_summary['q75'], alpha=0.2)

ax.plot(It_summary.index, It_summary['median'], label="Median")
ax.fill_between(It_summary.index, It_summary['q25'], It_summary['q75'], alpha=0.2, label="IQR")

# ax.plot(Rt_summary.index, Rt_summary['median'], label="R (Median)")
# ax.fill_between(Rt_summary.index, Rt_summary['q25'], Rt_summary['q75'], alpha=0.2)


ax.set_title("", loc='left')
ax.set_xlabel("Timestep")
ax.set_ylabel("$I_t$")

ax.legend(loc='upper right', frameon=False)

fig.tight_layout()
# plt.savefig('fig/fig_name.pdf', bbox_inches='tight')
plt.close()
show_fig_svg(fig)
output plot
# Covert 2d array to dictionary by time
It_dict_sim = {t: It_2darray_sim[t] for t in range(len(It_2darray_sim))}
# Calculate probability for each t
pn_dict_sim = {}
for t, data in It_dict_sim.items():
p_n = {int(It): It_count/len(data) for It, It_count in dict(Counter(It_dict_sim[t])).items() } # Count each I and calculate probability
p_n = {k: p_n[k] for k in sorted(p_n)} # Sort by key
pn_dict_sim[t] = p_n