The data consists of the daily number of sick students (listed “in bed”) over the course of an influenza outbreak that occurred at a British boarding school in 1978, a relatively closed community. In this example, we will model this outbreak using a Susceptible-Infected-Resistant (SIR) model.
Figure 1: The daily number of sick students over the course of the outbreak.
date
in_bed
t
0
1978-01-22
3
0
1
1978-01-23
8
1
2
1978-01-24
26
2
3
1978-01-25
76
3
4
1978-01-26
225
4
5
1978-01-27
298
5
6
1978-01-28
258
6
7
1978-01-29
233
7
8
1978-01-30
189
8
9
1978-01-31
128
9
10
1978-02-01
68
10
11
1978-02-02
29
11
12
1978-02-03
14
12
13
1978-02-04
4
13
Model
The time evolution of the susceptible population \(S(t)\), the infected (and infectious) population \(I(t)\), and the resistant population \(R(t)\) of students is linked. Let’s assume that when a susceptible student comes into contact with an infected one, the former can become infected for some time, and will become resistant (immune) after recovery. If \(\beta\) is the infection rate, \(\gamma\) the recovery rate, and the total number of students \(N=S+I+R=763\) does not change, then:
def sir(y, t, theta, n): S, I, R = y beta, gamma = theta dS_dt =-beta * I * S / n dI_dt = beta * I * S / n - gamma * I dR_dt = gamma * Ireturn jnp.array([dS_dt, dI_dt, dR_dt])
Let’s also assume that the outbreak started with one infected student, so that \(I(0)=1\), \(R(0)=0\), and \(S(0)=N-I(0)\). As the sampling distribution for the number of infected students, we will use the negative binomial distribution, which allows us to take overdispersion of the counts into account (relative to a Poisson distribution), by means of an additional parameter \(\phi\).
Figure 4: The predicted number of infected students (\(I\)), as well as the number of susceptible students (\(S\)) and resistant students (\(R\)), over the course of the outbreak, shown together with the observed counts (\(\hat{I}\)).
Figure
def plot_posterior(ax=None):if ax isNone: _, ax = plt.subplots(2, 1, figsize=FIGSIZE) post = az.extract(inf_data, var_names=["R0", "tau"]) post_hdi = az.hdi(inf_data, var_names=["R0", "tau"])for i, (name, values) inenumerate(post.items()): ax[i].hist(values, bins=50, density=True)for value in post_hdi[name]: ax[i].axvline(x=value, c="gray", ls="dotted") ax[i].set_xlabel(LABELLER.var_name_to_str(name)) ax[i].set_ylabel("Probability Density")return axplot_posterior()plt.tight_layout()plt.show()
Figure 5: The posterior distributions of the basic reproduction number \(R_0=\beta/\gamma\) and the recovery time \(\tau=1/\gamma\).