S = number of susceptible individuals in the population
I = number of infected individuals in the population
R = number of recovered/removed individuals in the population
N = total number of individuals in the population (S + I + R)
c = average number of contacts a susceptible makes per time
p = probability of a susceptible becoming infected on contact with infected person
beta β = infection rate (function of c * p)
gamma γ = recovery rate
lambda λ = rate of movement from exposed to infectious (beta * I/N)
R0 R-naught = basic reproduction number of infectious agents (beta / gamma)
Reff Rt = Effective Reproduction Number (R0 * S/N)
pvacc p = vaccination rate
pvacc_threshold pc = critical vaccination threshold (1 - (1/R0))
sigma σ = waning immunity rate
mu μ = background mortality rate
birth b = immigration or birth rate (same as mu if stable population)
M = number of deceased (if used)
Find out how long it takes for a cohort of infected people to recover. You need to keep track of 2 populations: those that are infected (compartment \(I\)), and those that have recovered (compartment \(R\)). Infected people recover at a rate \(\gamma\) (gamma). The differential equations describing this are:
\[\begin{align} \frac{dI}{dt} & = -\gamma I \\ \frac{dR}{dt} & = \gamma I \end{align}\]
We are looking at a cohort of 10\(^6\) currently infected people, and no one has recovered so far. The average duration of infection is 10 days. The question we want to answer is how many people will recover from the infection over a 4-week period.
nicesubtitle <- "Modelling an infected cohort"
print(nicesubtitle)
## [1] "Modelling an infected cohort"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
initial_number_infected <- 1000000 # the initial infected population
# size
initial_number_recovered <- 0 # the initial number of people in
# the recovered state
recovery_rate <- 10 ** (-1) # the rate of recovery gamma,
# in units of days^-1
follow_up_duration <- 7 * 4 # the duration to run the model for,
# in units of days
# The initial state values are stored as a vector
# and each value is assigned a name.
initial_state_values <- c(I = initial_number_infected,
R = initial_number_recovered)
# Parameters are also stored as a vector with assigned names and values.
parameters <- c(gamma = recovery_rate)
# In this case we only have one parameter, gamma.
# The times vector creates a sequence of timepoints at which we want to
# calculate the number of people in the I and R compartment.
times <- seq(from = 0, to = follow_up_duration, by = 1)
initial_state_values
## I R
## 1e+06 0e+00
parameters
## gamma
## 0.1
times
## [1] 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
## [26] 25 26 27 28
cohort_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
dI <- -(parameters['gamma']) * state['I']
dR <- parameters['gamma'] * state['I']
return(list(c(dI, dR)))
})
}
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = cohort_model,
parms = parameters))
print("recovered")
## [1] "recovered"
output['R'][29,]
## [1] 939189.9
print("proportion of total population")
## [1] "proportion of total population"
output['R'][29,] / initial_number_infected
## [1] 0.9391899
Plot your model with time on the x axis and the number of infected and recovered people on the y axis.
# First turn the output dataset into a long format,
# so that the number in each compartment at each timestep
# are all in the same column
output_long <- melt(as.data.frame(output), id = "time")
# Plot the number of people in each compartment over time
ggplot(data = output_long, # specify object containing data to plot
aes(x=time, y=value,color=variable)) + # assign columns to axes and groups
geom_line() + # represent data as lines
xlab("Time (days)")+ # add label for x axis
ylab("Number of people")+ # add label for y axis
labs(title=paste("gamma of",recovery_rate))
print("roughly right before 7 days")
## [1] "roughly right before 7 days"
output %>%
mutate(differs = abs(I - R),
proportion = R/(I + R)) %>%
arrange(differs, time) %>%
head(1)
## time I R differs proportion
## 1 7 496585.3 503414.7 6829.4 0.5034147
Vary \(\gamma\) to see how it affects the output. Change gamma (\(\gamma\)) to correspond to an average infectious period of:
a) 2 days
b) 20 days.
What is the recovery rate in these 2 cases?
print("average infectious period of 2 days")
## [1] "average infectious period of 2 days"
(recovery_rate <- 2 ** (-1))
## [1] 0.5
parameters <- c(gamma = recovery_rate)
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = cohort_model,
parms = parameters))
# Since the initial number in each compartment and
# the timesteps of interest haven't changed,
# these are the only parts of the code we need to rerun.
# Now, copy-paste your plot code from above here to visualise the output.
melt(as.data.frame(output), id = "time") %>%
ggplot(aes(x=time, y=value,color=variable)) +
geom_line() +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste("gamma of",recovery_rate))
print("closest to all recovered")
## [1] "closest to all recovered"
output %>%
mutate(differs = abs(I - R),
proportion = R/(I + R)) %>%
arrange(-R, time) %>%
head(1)
## time I R differs proportion
## 1 28 0.8315196 999999.2 999998.3 0.9999992
print("average infectious period of 20 days")
## [1] "average infectious period of 20 days"
(recovery_rate <- 20 ** (-1))
## [1] 0.05
parameters <- c(gamma = recovery_rate)
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = cohort_model,
parms = parameters))
# Since the initial number in each compartment and
# the timesteps of interest haven't changed,
# these are the only parts of the code we need to rerun.
# Now, copy-paste your plot code from above here to visualise the output.
melt(as.data.frame(output), id = "time") %>%
ggplot(aes(x=time, y=value,color=variable)) +
geom_line() +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste("gamma of",recovery_rate))
print("closest to all recovered")
## [1] "closest to all recovered"
output %>%
mutate(differs = abs(I - R),
proportion = R/(I + R)) %>%
arrange(-R, time) %>%
head(1)
## time I R differs proportion
## 1 28 246597 753403 506806.1 0.753403
We will focus on adding disease-induced mortality to this model in order to explore the concept of competing hazards. You will also calculate the case fatality ratio, and compare it against the result.
The model we want to specify has 3 compartments: \(I\) (infected), \(R\) (recovered) and \(M\) (dead).
The differential equations for the model look like this: \[\begin{align} \frac{dI}{dt} & = -\gamma I -\mu I \\ \frac{dR}{dt} & = \gamma I \\ \frac{dM}{dt} & = \mu I \end{align}\]
This corresponds to the following model diagram:
The equation describing the rate of change in the recovered (\(R\)) compartment (second line) is not affected by this addition. However, we need a new equation describing the rate of change in the deceased (\(M\)) compartment (third line). People move into this compartment from the infected compartment (\(I\)) at a rate \(\mu\) - this transition also needs to be added in the rate of change in the infected compartment \(I\) (first line).
Incorporate the new compartment and transition. At the start, there are 10\(^6\) infected people. No one has recovered or died yet. The recovery rate \(\gamma\) is 0.1 days\(^{-1}\) and the mortality rate \(\mu\) is 0.2 days\(^{-1}\). We want to model the course of the infection over 4 weeks.
nicesubtitle <- "Simulating competing hazards v1"
print(nicesubtitle)
## [1] "Simulating competing hazards v1"
print("initial state values and parameters")
## [1] "initial state values and parameters"
initial_state_values <- c(I = 1000000,
R = 0,
M = 0)
parameters <- c(gamma = 0.1,
mu = 0.2)
times <- seq(from = 0, to = (7 * 4), by = 1)
# Look back at the code in the previous activity if you cannot
# remember how to define these vectors.
cohort_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
dI <- (-gamma * I) - (mu * I)
dR <- gamma * I
dM <- mu * I
return(list(c(dI, dR, dM)))
})
}
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = cohort_model,
parms = parameters))
melt(as.data.frame(output), id = "time") %>%
ggplot(aes(x=time, y=value,color=variable,shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("red","green","black")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste("gamma of", parameters['gamma'],
" mu of", parameters['mu']),color = "Compartment",
subtitle= nicesubtitle)
# Answer: deaths/initial number ~approx .67
output[output$time == 28,"M"] / output[output$time == 0,"I"]
## [1] 0.6665168
# Answer: Number is close
# Case fatality rate is mu divided by mu + gamma ~approx .67
parameters['mu']/(parameters['mu']+parameters['gamma'])
## mu
## 0.6666667
.5 = mu/(mu + gamma);
.5mu + .5gamma = mu;
.5gamma = mu -.5mu;
.5gamma = .5mu;
gamma = mu
# Which value of mu do you need to get a case fatality rate of 50% assuming gamma stays fixed?
# Answer: .5 = mu/(mu + gamma);
# .5mu + .5gamma = mu;
# .5gamma = mu -.5mu;
# .5gamma = .5mu;
# gamma = mu
parameters['mu'] <- parameters['gamma']
parameters
## gamma mu
## 0.1 0.1
Simulate the model using using the \(\mu\) value that you have just calculated, and verify that the code gives you a CFR of 50%.
nicesubtitle <- "Simulating competing hazards v2"
print(nicesubtitle)
## [1] "Simulating competing hazards v2"
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = cohort_model,
parms = parameters))
melt(as.data.frame(output), id = "time") %>%
ggplot(aes(x=time, y=value,color=variable,shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("red","green","black")) +
scale_shape_manual(values = c(0, 4, 1)) +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste("gamma of", parameters['gamma'],
" mu of", parameters['mu']),color = "Compartment",
subtitle= nicesubtitle)
print("deaths/initial number ~approx .498")
## [1] "deaths/initial number ~approx .498"
output[output$time == 28,"M"] / output[output$time == 0,"I"]
## [1] 0.4981511
print("Case fatality rate is mu divided by mu + gamma is .5")
## [1] "Case fatality rate is mu divided by mu + gamma is .5"
parameters['mu']/(parameters['mu']+parameters['gamma'])
## mu
## 0.5
Start coding a simplified SIR model within R.
SIR model:
The differential equations for this system are:
\[\begin{align} \frac{dS}{dt} & = -\lambda S \\ \frac{dI}{dt} & = \lambda S - \gamma I \\ \frac{dR}{dt} & = \gamma I \end{align}\]
We can use the SIR model to describe a disease that can be split into 3 states: susceptible (\(S\)), infected (\(I\)), or recovered (R). All those infected are infectious, and all those recovered are immune, so they cannot get the disease again.
One part of this model was already discussed: the transition from I to R. The new addition is the susceptibles in compartment S. Depending on how many people in the population are infectious, susceptible people experience a force of infection \(\lambda\) (lambda), which is the transition rate at which they become infected.
We are trying to simulate an outbreak of a new infectious disease that our population of 10\(^{6}\) people has not been exposed to before. This means that we are starting with a single case, everyone else is susceptible to the disease, and no one is yet immune or recovered. This can for example reflect a situation where an infected person introduces a new disease into a geographically isolated population, like on an island, or even when an infections “spill over” from other animals into a human population. In terms of the initial conditions for our model, we can define: S = 10\(^{6}\)-1 = 999999, I = 1 and R = 0.
Solve this model assuming a constant force of infection of 0.2 days\(^{-1}\). The infection has an average duration of 10 days, but we want to run the model for 60 days.
nicesubtitle <- "SIR Model with a Constant Force of Infection"
print(nicesubtitle)
## [1] "SIR Model with a Constant Force of Infection"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
initial_state_values <- c(S = (1000000 - 1),
I = 1,
R = 0)
parameters <- c(gamma = 10 **-1,
lambda = 0.2)
# TIMESTEPS:
times <- seq(from = 0, to = 60, by = 1)
# SIR MODEL FUNCTION
# We are renaming this to sir_model.
# Remember to include the input arguments,
# differential equations and output objects here.
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
dS <- -(lambda * S)
dI <- (lambda * S) -(gamma * I)
dR <- (gamma * I)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters))
Plot the number of people in each compartment over time. You should see that at the peak of the epidemic, around 500000 people are infected.
print("Plotting the number of people in each compartment over time")
## [1] "Plotting the number of people in each compartment over time"
melt(as.data.frame(output), id = "time") %>%
ggplot(aes(x=time, y=value,color=variable,shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste("gamma of", parameters['gamma'],
" lambda of", parameters['lambda']),color = "Compartment",
subtitle= nicesubtitle)
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R
## 1 7 246596.7 499976.7 253426.6
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R
## 61 60 6.144158 4945.213 995048.6
print("at day 60, proportion = recovered/initial number ~approx 99.5%")
## [1] "at day 60, proportion = recovered/initial number ~approx 99.5%"
output[output$time == 60,"R"] / output[output$time == 0,"S"]
## [1] 0.9950496
print("attempt to figure out end of I by extending time, and day 145 looks like the closest to absolute recovery")
## [1] "attempt to figure out end of I by extending time, and day 145 looks like the closest to absolute recovery"
(as.data.frame(ode(y = initial_state_values,
times = seq(from = 0, to = 150, by = 1),
func = sir_model,
parms = parameters))) %>%
mutate(differs = abs(initial_state_values['S'] - R)) %>%
arrange(differs, time) %>%
head(1)
## time S I R differs
## 1 145 2.543637e-07 1.008694 999999 0.008694556
The previous activity introduced the concept of the force of infection. However, it made the unrealistic assumption that the force of infection can be expressed as a constant rate. In reality \(\lambda\) changes over time, depending on the number of infected people in the population.
The diagram for a simple SIR model looks as follows, with \(\lambda\) defined as a function of the infection rate, \(\beta\) (beta), and the proportion of the population that is infectious, \(I/N\).
The differential equations for an SIR model with a dynamic force of infection are:
\[\begin{align} \frac{dS}{dt} & = -\beta \frac{I}{N} S \\ \\ \frac{dI}{dt} & = \beta \frac{I}{N} S - \gamma I \\ \\ \frac{dR}{dt} & = \gamma I \end{align}\]
Some assumptions inherent in this model structure are:
Simulate this SIR model and extend it to capture a dynamic force of infection. Our assumptions for the initial conditions (\(S = 999999\), \(I = 1\) and \(R = 0\)), simulation period (60 days) and \(\gamma\) (0.1 days\(^{-1}\)) remain the same as in the previous activity, and the daily infection rate \(\beta\) equals 1.
Plot the curves of infection, susceptibility and recovery over time.
nicesubtitle <- "SIR Model with a Dynamic Force of Infection"
print(nicesubtitle)
## [1] "SIR Model with a Dynamic Force of Infection"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
initial_state_values <- c(S = 1000000 - 1,
I = 1,
R = 0)
parameters <- c(gamma = .1,
beta = 1)
# TIMESTEPS:
times <- seq(from = 0, to = 60, by = 1)
# SIR MODEL FUNCTION
sir_model2 <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S)
dI <- (lambda * S) -(gamma * I)
dR <- (gamma * I)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model2,
parms = parameters))
# PLOT
melt(as.data.frame(output), id = "time") %>%
ggplot(aes(x=time, y=value,color=variable,shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste("gamma of", parameters['gamma'],
" beta of", parameters['beta']),color = "Compartment",
subtitle= nicesubtitle)
# Q: After how many days does the epidemic peak? What is the peak prevalence?
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R
## 1 18 100188.4 669741.4 230070.2
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R
## 61 60 51.14455 11864.48 988084.4
Explore how your epidemic curve changes under different assumptions for \(\beta\) and \(\gamma\).
nicesubtitle <- "SIR Model with a Dynamic Force of Infection v2"
print(nicesubtitle)
## [1] "SIR Model with a Dynamic Force of Infection v2"
print("initial state values and parameters")
## [1] "initial state values and parameters"
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
# tell R to look for variable names within the
# state and parameters objects
N <- S+I+R
# New: defining lambda as a function of beta and I:
lambda <- beta * I/N
# The differential equations
dS <- -lambda * S # people move out of (-) the
# S compartment at a rate lambda
# (force of infection)
dI <- lambda * S - gamma * I # people move into (+) the I compartment
# from S at a rate lambda,
# and move out of (-) the I compartment
# at a rate gamma (recovery)
dR <- gamma * I # people move into (+) the R compartment
# from I at a rate gamma
# Return the number of people in the S, I and R compartments at each
# timestep
# (in the same order as the input state variables)
return(list(c(dS, dI, dR)))
})
}
run_sir_model <-
function(beta, gamma, S0 = 999999, I0 = 1, R0 = 1, duration = 60) {
initial_state_values <- c(S = S0, # nearly the whole population we are
# modelling is susceptible to infection
I = I0, # the epidemic starts with a single
# infected person
R = R0) # there is no prior immunity in the
# population
parameters <- c(beta = beta, # the rate of infection,
gamma = gamma) # the rate of recovery,
# which acts on those infected
times <- seq(from = 0, to = duration, by = 1)
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters))
print("Plotting the proportion of people in each compartment over time")
plot(x = output$time, # time on the x axis
y = output$S, # the number of susceptible people at
col = "blue", # each timestep on the y axis
type = "l", # type = "l" tells R we want lines
# rather than points
ylim = c(0,(S0+I0+R0)), # the limits of the y axis
# (from 0 to the total number of
# people)
xlab = "Time (days)",
ylab = "Number of people") # add axis labels
lines(x = output$time, # add the number of
y = output$I, # infected people at each
col = "red") # timestep on the y axis
lines(x = output$time, # number of recovered
y = output$R, # people at each
col = "green") # timestep on the y axis
legend(x = "right", # add a legend on the right-hand
# side of the plot
legend = c("S", "I", "R"), # labels S, I and R for blue,
col = c("blue", "red", "green"), # red, green lines respectively
lty = c(1,1)) # both lines are
# solid linetype (lty = 1)
title(main = paste("beta =", beta, # main title
", gamma =", gamma))
}
def.par <- par(no.readonly = TRUE)
# Prepare the format for the plot display
# par(mfrow = c(2,4)) # Show 2*4 separate plots in one page
# (2 rows, 4 per row)
par(mfrow = c(2,2))
# Run the model and plot the output with different beta and gamma values.
# You might also have to change the time that you run the model for,
# by changing the value of the duration argument,
# in units of days (for example, to reproduce your model above,
# this would be duration = 60).
run_sir_model(beta = 1,
gamma = .1,
duration = 60)
## [1] "Plotting the proportion of people in each compartment over time"
run_sir_model(beta = 1,
gamma = .5,
duration = 60)
## [1] "Plotting the proportion of people in each compartment over time"
run_sir_model(beta = 3,
gamma = .1,
duration = 60)
## [1] "Plotting the proportion of people in each compartment over time"
run_sir_model(beta = 3,
gamma = .5,
duration = 60)
## [1] "Plotting the proportion of people in each compartment over time"
# Repeat the run_sir_model() command here for every parameter combination
# you want to try, and adapt the par() command above depending
# on how many plots you want to look at
par(def.par) #- reset to default
Different recovery rates affect the epidemic just as much as different forces of infection. With \(\beta\) held constant at 1, an increasing value for \(\gamma\) tends to lead to a later and lower peak of infected people, and an earlier rise in the recovered curve. If people can stay infected for a long time before recovering (\(\gamma\) = 0.025, corresponding to an average duration of infection of 40 days), the number of infected people stays high over a longer period and declines slowly - the epidemic flattens out. In contrast, if recovery happens very quickly after infection (\(\gamma\) = 0.5), there is a only small peak in the prevalence of infection and the epidemic dies out quickly. If \(\gamma\) is as large as 1, no epidemic takes place after the introduction of 1 infected case.
The roles of \(\beta\) and \(\gamma\).
So far, when looking at the dynamics of an epidemic, we have always plotted the number of people in each compartment. Sometimes however it is more informative to look at the proportion of the population in each compartment rather than the actual number, particularly if the population size changes over time. Although we are modelling an epidemic in a closed population with constant population size in this activity, it is a good opportunity to practice plotting the prevalence as a proportion of the total population who are in the \(S\), \(I\) and \(R\) compartments.
Model the epidemic with the \(\beta\) and \(\gamma\) values you calculated above, assuming introduction of a single infected person in a totally susceptible population of 1 million. Then plot the proportion of susceptible, infected and recovered people over time.
nicesubtitle <- "SIR dynamics with varying parameters v1"
print(nicesubtitle)
## [1] "SIR dynamics with varying parameters v1"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
initial_state_values <- c(S = 1000000 - 1,
I = 1,
R = 0)
(parameters <- c(beta = 1/2,
gamma = 1/4))
## beta gamma
## 0.50 0.25
# TIMESTEPS:
times <- seq(from = 0, to = 120, by = 1)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S)
dI <- (lambda * S) -(gamma * I)
dR <- (gamma * I)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100)
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re
## 1 55 489187.4 153308.3 357504.3 48.919 15.331 35.75
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re
## 121 120 203205.6 26.39732 796768 20.321 0.003 79.677
print("Plotting the number of people in each compartment over time")
## [1] "Plotting the number of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re) %>%
melt(id = "time") %>%
ggplot(aes(x=time, y=value,color=variable,shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste("gamma of", parameters['gamma'],
" beta of", parameters['beta']),color = "Compartment",
subtitle= nicesubtitle)
Imagine an intervention is introduced to control infection, for example infected people are isolated so that they cannot spread infection. As a result, \(\beta\) drops to 0.1. Model the epidemic under this new scenario.
nicesubtitle <- "SIR dynamics with varying parameters v2"
print(nicesubtitle)
## [1] "SIR dynamics with varying parameters v2"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
initial_state_values <- c(S = 1000000 - 1,
I = 1,
R = 0)
(parameters <- c(beta = .1,
gamma = 1/4))
## beta gamma
## 0.10 0.25
# TIMESTEPS:
times <- seq(from = 0, to = 700, by = 1)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S)
dI <- (lambda * S) -(gamma * I)
dR <- (gamma * I)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100)
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re
## 1 0 999999 1 0 100 0 0
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re
## 701 700 999998.3 9.971222e-44 1.666665 100 0 0
print("Plotting the number of people in each compartment over time")
## [1] "Plotting the number of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re) %>%
melt(id = "time") %>%
ggplot(aes(x=time, y=value,color=variable,shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste("gamma of", parameters['gamma'],
" beta of", parameters['beta']),color = "Compartment",
subtitle= nicesubtitle)
Now, vary \(\gamma\) with \(\beta\) held constant at 0.1 days\(^{-1}\). Notice that there are some values where the initial infected case fails to cause an epidemic.
As the infection rate becomes lower, i.e. the infection spreads more slowly, you have to follow the population over a longer period to observe a visible change in the proportion infected. You should see that different combinations of infection and recovery rates can give rise to very different infection dynamics.
nicesubtitle <- "SIR dynamics with varying parameters v3"
print(nicesubtitle)
## [1] "SIR dynamics with varying parameters v3"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
initial_state_values <- c(S = 1000000 - 1,
I = 1,
R = 0)
(parameters <- c(beta = .1,
gamma = .01))
## beta gamma
## 0.10 0.01
# TIMESTEPS:
times <- seq(from = 0, to = 700, by = 1)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S)
dI <- (lambda * S) -(gamma * I)
dR <- (gamma * I)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100)
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re
## 1 180 100188.8 669741.4 230069.8 10.019 66.974 23.007
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re
## 701 700 47.44886 4366.841 995585.7 0.005 0.437 99.559
print("Plotting the number of people in each compartment over time")
## [1] "Plotting the number of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re) %>%
melt(id = "time") %>%
ggplot(aes(x=time, y=value,color=variable,shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste("gamma of", parameters['gamma'],
" beta of", parameters['beta']),color = "Compartment",
subtitle= nicesubtitle)
In the simple SIR model, we get an epidemic only if \(\beta\)/\(\gamma\) > 1, i.e. if the average number of secondary cases caused by a single infected case in a totally susceptible population is greater than 1. As susceptibility declines over the course of the epidemic, the effective reproduction number Reff determines the shape of the epidemic curve as it reflects the amount of immunity in the population at any given time.
In a simple homogenous SIR model, Reff is directly related to the proportion of the population that is susceptible: \[\begin{align} R_{eff} = R_{0} \frac{S}{N} \end{align}\]
Model an epidemic and study the connection between the behaviour of Reff and the epidemic curve. We are looking at a closed fully susceptible population, into which a single infected person is introduced. Chose your combination of parameters so that R0 > 1. Solve the differential equations, and use the output to calculate Reff at each timestep. Generate 2 plots for comparison: one of the proportion of susceptible, infected and recovered individuals over time, and one of the effective reproduction number over time.
nicesubtitle <- "Simulating the effective reproduction number R eff v1"
print(nicesubtitle)
## [1] "Simulating the effective reproduction number R eff v1"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
initial_state_values <- c(S = 1000000-1,
I = 1,
R = 0)
duration <- 150
beta <- 1/2
gamma <- 1/4
R0 <- beta / gamma
(parameters <- c(beta = beta,
gamma = gamma,
R0 = R0))
## beta gamma R0
## 0.50 0.25 2.00
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = 1)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S)
dI <- (lambda * S) -(gamma * I)
dR <- (gamma * I)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 55 489187.4 153308.3 357504.3 48.919 15.331 35.75 0.9783747
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(-Reff, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 55 489187.4 153308.3 357504.3 48.919 15.331 35.75 0.9783747
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 151 150 203187.7 0.3076275 796812 20.319 0 79.681 0.4063754
print("Plotting the number of people in each compartment over time")
## [1] "Plotting the number of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
ggplot(aes(x=time, y=value,color=variable,shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0']),
color = "Compartment",
subtitle= nicesubtitle)
print("2 y-axis plot, where S and R eff overlap")
## [1] "2 y-axis plot, where S and R eff overlap"
output2 <- output %>%
mutate(Reff_ = Reff / parameters['R0']) %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = ifelse(variable == "Reff_", value, value / sum(initial_state_values)))
ggplot(output2, aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line(linetype="dotdash", aes(y=proportion)) +
scale_y_continuous(sec.axis= sec_axis(~.* parameters['R0'], name="R effective")) +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green","orange")) +
scale_shape_manual(values = c(0,4,1,6)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
nicesubtitle <- "Simulating the effective reproduction number R eff v2"
print(nicesubtitle)
## [1] "Simulating the effective reproduction number R eff v2"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
initial_state_values <- c(S = 1000000-1,
I = 1,
R = 0)
duration <- 100
beta <- 0.4
gamma <- .1
R0 <- beta / gamma
(parameters <- c(beta = beta,
gamma = gamma,
R0 = R0))
## beta gamma R0
## 0.4 0.1 4.0
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = 1)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S)
dI <- (lambda * S) -(gamma * I)
dR <- (gamma * I)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 50 258083.8 403299 338617.2 25.808 40.33 33.862 1.032335
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(-Reff, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 51 219671.7 401423.5 378904.8 21.967 40.142 37.89 0.8786866
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 101 100 20473.64 7372.583 972153.8 2.047 0.737 97.215 0.08189457
print("Plotting the number of people in each compartment over time")
## [1] "Plotting the number of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
ggplot(aes(x=time, y=value,color=variable,shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0']),
color = "Compartment",
subtitle= nicesubtitle)
print("2 y-axis plot, where S and R eff overlap")
## [1] "2 y-axis plot, where S and R eff overlap"
output2 <- output %>%
mutate(Reff_ = Reff / parameters['R0']) %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = ifelse(variable == "Reff_", value, value / sum(initial_state_values)))
ggplot(output2, aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line(linetype="dotdash", aes(y=proportion)) +
scale_y_continuous(sec.axis= sec_axis(~.* parameters['R0'], name="R effective")) +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green","orange")) +
scale_shape_manual(values = c(0,4,1,6)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
We want to incorporate births and deaths into the basic SIR model. The structure for this model looks like this:
The differential equations for an SIR model with population turnover look like this:
\[\begin{align} \frac{dS}{dt} & = -\beta \frac{I}{N} S - \mu S + bN \\ \frac{dI}{dt} & = \beta \frac{I}{N} S - \gamma I - \mu I \\ \frac{dR}{dt} & = \gamma I - \mu R \end{align}\]
This structure assumes that every individual in each compartment experiences the same background mortality \(\mu\) (there is no additional mortality from the infection for example, and we make no distinction by age). Those who have died no longer contribute to infection (a sensible assumption for many diseases). Babies are all born at a rate b into the susceptible compartment.
Note that, as always, we calculate the people dying in each of the compartments by multiplying the number in that compartment by the rate \(\mu\). Even though babies are all born into the same compartment, the birth rate still depends on the population in all of the compartments, hence why we need to multiply b by the total population size N. In the following activity, we choose a value of b to allow a constant population size, so that all deaths are balanced by births.
Modelling an acute disease epidemic in a fully susceptible human population
Parameters:
\(\beta\) = 0.4 days\(^{-1}\) = 0.4 * 365 years\(^{-1}\)
\(\gamma\) = 0.2 days\(^{-1}\) = 0.2 * 365 years\(^{-1}\)
\(\mu\) = 1/70 years\(^{-1}\)
b = 1/70 years\(^{-1}\)
Extend the SIR model to capture this population turnover process. In the first instance, we are looking at an acute disease epidemic introduced into a fully susceptible human population. The infection and recovery rates are 0.4 and 0.2 days\(^{-1}\) respectively. We can calculate the background mortality rate based on the average human lifespan - let’s assume that this is 70 years, so mu = 1/70 years\(^{-1}\), or 1/(70x365) days\(^{-1}\). In this example, we are also assuming that the population size stays constant over time at 10\(^{6}\) - to achieve that, we set the birth rate b = mu.
Code this model and run it first for 1 year, and plot only the number of infected people over time at this point. Confirm that you observe your regular SIR epidemic curve before proceeding.
nicesubtitle <- "Modelling population turnover v1, 365 days"
print(nicesubtitle)
## [1] "Modelling population turnover v1, 365 days"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
initial_state_values <- c(S = 1000000-1,
I = 1,
R = 0)
duration <- 365
tsteps <- 1
beta <- 0.4
gamma <- 0.2
mu <- 1/(70 * 365)
birth <- mu
R0 <- beta / gamma
(parameters <- c(
beta = beta,
gamma = gamma,
R0 = R0,
mu = mu,
birth = birth
))
## beta gamma R0 mu birth
## 4.000000e-01 2.000000e-01 2.000000e+00 3.913894e-05 3.913894e-05
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S) -(S * mu) +(N * birth)
dI <- (lambda * S) -(gamma * I) -(I * mu)
dR <- (gamma * I) -(R * mu)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 68 512728.7 153245.8 334025.5 51.273 15.325 33.403 1.025457
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 69 482248.6 153076.3 364675.1 48.225 15.308 36.468 0.9644972
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 366 365 212026.6 3.463514e-10 787973.4 21.203 0 78.797 0.4240533
print("Plotting the number of people in each compartment over time")
## [1] "Plotting the number of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
ggplot(aes(x=time, y=value,color=variable,shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle)
Now, change the duration of the model run to several generations, for example 400 years.
A note on timesteps:
So far, we have only modelled short-term disease dynamics, where it was intuitive to express the timesteps in units of days. In this example, the simulation timescale is much longer, so it might be easier to change the units to years (although the result is also correct if we model 400 years as 146000 days). Now, we need to start thinking about the interval of the timesteps, in the code below.
nicesubtitle <- "Modelling population turnover v2, \n 400 years, daily intervals step2"
print(nicesubtitle)
## [1] "Modelling population turnover v2, \n 400 years, daily intervals step2"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# times <- seq(from = 0, to = 400, by = 1/365) # from 0 to 400 YEARS in DAILY intervals
# MODEL INPUTS:
initial_state_values <- c(S = 1000000-1,
I = 1,
R = 0)
duration <- 365 * 400
tsteps <- 2
beta <- 0.4
gamma <- 0.2
mu <- 1/(70 * 365)
birth <- mu
R0 <- beta / gamma
(parameters <- c(
beta = beta,
gamma = gamma,
R0 = R0,
mu = mu,
birth = birth
))
## beta gamma R0 mu birth
## 4.000000e-01 2.000000e-01 2.000000e+00 3.913894e-05 3.913894e-05
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S) -(S * mu) +(N * birth)
dI <- (lambda * S) -(gamma * I) -(I * mu)
dR <- (gamma * I) -(R * mu)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 68 512728.3 153246 334025.7 51.273 15.325 33.403 1.025457
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 70 453778.1 151113.9 395108 45.378 15.111 39.511 0.9075561
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 73001 146000 503432.5 69.27998 496498.2 50.343 0.007 49.65 1.006865
print("Plot Infection only - 400 days: ")
## [1] "Plot Infection only - 400 days: "
output %>%
filter(time <= 400) %>%
select(time, I) %>%
ggplot(aes(x=time, y=I)) +
geom_line(linetype="dotdash", color="red") +
geom_jitter(color="red", shape=4, show.legend = FALSE) +
xlab("Time (days)") +
ylab("I Infected") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle)
print("Plot Infection only - all days: ")
## [1] "Plot Infection only - all days: "
output %>%
select(time, I) %>%
ggplot(aes(x=time, y=I)) +
geom_line(linetype="dotdash", color="red") +
geom_jitter(color="red", shape=4, show.legend = FALSE) +
xlab("Time (days)") +
ylab("I Infected") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle)
print("Plotting the number of people in each compartment over time")
## [1] "Plotting the number of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
ggplot(aes(x=time, y=value,color=variable,shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle)
Before investigating the disease dynamics, play around with the timesteps at which you are solving the model in your code above and plot the output. In the cell below, it is daily - also try solving it only at every 2, 3, 4 and 5 days. Keep in mind that as we are running the model for longer now, it might take your computer longer to produce the output.
nicesubtitle <- "Modelling population turnover v3, \n 400 years, yearly intervals"
print(nicesubtitle)
## [1] "Modelling population turnover v3, \n 400 years, yearly intervals"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# times <- seq(from = 0, to = 400, by = 1/365) # from 0 to 400 YEARS in DAILY intervals
# MODEL INPUTS:
initial_state_values <- c(S = 1000000-1,
I = 1,
R = 0)
duration <- 400
tsteps <- 1/365
beta <- 0.4 * 365
gamma <- 0.2 * 365
mu <- 1/(70 * 1)
birth <- mu
R0 <- beta / gamma
(parameters <- c(
beta = beta,
gamma = gamma,
R0 = R0,
mu = mu,
birth = birth
))
## beta gamma R0 mu birth
## 146.00000000 73.00000000 2.00000000 0.01428571 0.01428571
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S) -(S * mu) +(N * birth)
dI <- (lambda * S) -(gamma * I) -(I * mu)
dR <- (gamma * I) -(R * mu)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 0.1863014 512728.7 153245.8 334025.5 51.273 15.325 33.403 1.025457
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 0.1890411 482248.6 153076.3 364675.1 48.225 15.308 36.468 0.9644972
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 146001 400 503898.4 80.8277 496020.8 50.39 0.008 49.602 1.007797
print("Plotting the number of people in each compartment over time")
## [1] "Plotting the number of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
ggplot(aes(x=time, y=value,color=variable,shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (years)") +
ylab("Number of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (years)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle)
Finally, run and plot the number of infected people again with the timestep that seems most sensible based on your findings, to study the long-term disease dynamics.
Now make a plot that also shows the number of susceptible and recovered individuals, as a proportion of the total population.
# Redo using daily interval and plot n=400 then all
nicesubtitle <- "Modelling population turnover v4, redo"
print(nicesubtitle)
## [1] "Modelling population turnover v4, redo"
print("initial state values and parameters")
## [1] "initial state values and parameters"
nicesubtitle <- "Modelling population turnover"
# MODEL INPUTS:
initial_state_values <- c(S = 1000000-1,
I = 1,
R = 0)
duration <- 365 * 400
tsteps <- 2
beta <- 0.4
gamma <- 0.2
mu <- 1/(70 * 365)
birth <- mu
R0 <- beta / gamma
(parameters <- c(
beta = beta,
gamma = gamma,
R0 = R0,
mu = mu,
birth = birth
))
## beta gamma R0 mu birth
## 4.000000e-01 2.000000e-01 2.000000e+00 3.913894e-05 3.913894e-05
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S) -(S * mu) +(N * birth)
dI <- (lambda * S) -(gamma * I) -(I * mu)
dR <- (gamma * I) -(R * mu)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 68 512728.3 153246 334025.7 51.273 15.325 33.403 1.025457
# point when R eff goes below 1 - day / where R eff = ?
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 70 453778.1 151113.9 395108 45.378 15.111 39.511 0.9075561
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 73001 146000 503432.5 69.27998 496498.2 50.343 0.007 49.65 1.006865
#
# Limit to 400 days to see short term details
#
nicesubtitle <- "Modelling population turnover v4, redo n=400"
print(nicesubtitle)
## [1] "Modelling population turnover v4, redo n=400"
print("Plotting the number of people in each compartment over time")
## [1] "Plotting the number of people in each compartment over time"
output %>%
filter(time <= 400) %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
ggplot(aes(x=time, y=value,color=variable,shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
# Plotting the proportion of people in each compartment over time
output %>%
filter(time <= 400) %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
filter(time <= 400) %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle)
# 2 y-axis plot, where S and R eff overlap
output2 <- output %>%
filter(time <= 400) %>%
mutate(Reff_ = Reff / parameters['R0']) %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = ifelse(variable == "Reff_", value, value / sum(initial_state_values)))
ggplot(output2, aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line(linetype="dotdash", aes(y=proportion)) +
scale_y_continuous(sec.axis= sec_axis(~.* parameters['R0'], name="R effective")) +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green","orange")) +
scale_shape_manual(values = c(0,4,1,6)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
#
# Open up to long time period
#
nicesubtitle <- "Modelling population turnover v4, redo n=ALL"
print(nicesubtitle)
## [1] "Modelling population turnover v4, redo n=ALL"
print("Plotting the number of people in each compartment over time")
## [1] "Plotting the number of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
ggplot(aes(x=time, y=value,color=variable,shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
# Plotting the proportion of people in each compartment over time
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle)
# 2 y-axis plot, where S and R eff overlap
output2 <- output %>%
mutate(Reff_ = Reff / parameters['R0']) %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = ifelse(variable == "Reff_", value, value / sum(initial_state_values)))
ggplot(output2, aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line(linetype="dotdash", aes(y=proportion)) +
scale_y_continuous(sec.axis= sec_axis(~.* parameters['R0'], name="R effective")) +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green","orange")) +
scale_shape_manual(values = c(0,4,1,6)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
In the second example, we are modelling a similar acute disease, but this time we are changing the mortality and birth rates to represent a population with a much more rapid turnover. The infection parameters are the same as before, but we are assuming the average lifespan is 4 weeks this time.
Code this scenario below and run the model for 1 year, and plot the prevalence of susceptibility, infection and recovery over time.
# short life people scenario
nicesubtitle <- "Modelling population turnover v5, short life span"
print(nicesubtitle)
## [1] "Modelling population turnover v5, short life span"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
initial_state_values <- c(S = 1000000-1,
I = 1,
R = 0)
duration <- 365
tsteps <- 1
beta <- 0.4
gamma <- 0.2
mu <- 1/(7 * 4)
birth <- mu
R0 <- beta / gamma
(parameters <- c(
beta = beta,
gamma = gamma,
R0 = R0,
mu = mu,
birth = birth
))
## beta gamma R0 mu birth
## 0.40000000 0.20000000 2.00000000 0.03571429 0.03571429
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S) -(S * mu) +(N * birth)
dI <- (lambda * S) -(gamma * I) -(I * mu)
dR <- (gamma * I) -(R * mu)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 83 591760.7 131429.3 276810 59.176 13.143 27.681 1.183521
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 93 498195.6 105147.1 396657.3 49.82 10.515 39.666 0.9963913
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 366 365 589257.4 62237.4 348505.2 58.926 6.224 34.851 1.178515
print("Plotting the number of people in each compartment over time")
## [1] "Plotting the number of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
ggplot(aes(x=time, y=value,color=variable,shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Number of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
# Plotting the proportion of people in each compartment over time
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle)
# 2 y-axis plot, where S and R eff overlap
output2 <- output %>%
mutate(Reff_ = Reff / parameters['R0']) %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = ifelse(variable == "Reff_", value, value / sum(initial_state_values)))
ggplot(output2, aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line(linetype="dotdash", aes(y=proportion)) +
scale_y_continuous(sec.axis= sec_axis(~.* parameters['R0'], name="R effective")) +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green","orange")) +
scale_shape_manual(values = c(0,4,1,6)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
The examples we have modelled here can be compared to real-world diseases. An acute disease that causes a pattern of epidemic cycles in human populations like in the first example is measles. The second example on the other hand is more comparable to endemic swine flu on pig farms, where pigs enter and leave the farm in a matter of weeks (hence the ‘average lifespan’ that you modelled, of 4 weeks).
However, the basic SIR model is very simple and does not account for many of the factors that can affect disease dynamics in reality. For example, measles notifications data from the pre-vaccination era suggest that epidemic oscillations did not flatten out over time like in our model. This tells us that there are other processes that sustain these cycles, that we have not included in the model.
This activity was also your first look at a simple demographic process with births and deaths. As you might have noticed, the population size in this example stayed constant over time. However, if we were to model the demography of a country over time, an increasing population size is usually more realistic.
In this activity, we also made the assumption that all babies are born susceptible. Of course, this is not always the case in reality: maternal antibodies can confer immunity to newborns, neonatal vaccination can make newborns immune to infection, and some infections can be transmitted from mothers to their children, like HIV.
We are going back to the simple SIR model, without births or deaths, to look at the effect of vaccination in a very simple way – we are assuming it already happened before we run our model! By changing the initial conditions, we can prepare the population so that it has received a certain coverage of vaccination. Even though this is very simplistic, it will allow us to study some important effects of vaccination on the infection dynamics.
We are starting with the transmission and recovery parameters beta = 0.4 days\(^{-1}\) and gamma = 0.1 days \(^{-1}\). To incorporate immunity from vaccination in the model, we assume that a proportion p of the total population starts in the recovered compartment, representing the vaccine coverage and assuming the vaccine is perfectly effective. Again, we assume the epidemic starts with a single infected case introduced into the population.
Model this scenario for a duration of 2 years, assuming that the vaccine coverage is 50%, and plot the prevalence in each compartment over time. Confirm that you observe an epidemic peaking at around 125 days after introduction of the infectious person in the population. Also have a look at the proportion susceptible and recovered, to double-check this looks like what you would expect given your initial conditions.
Modelling a disease where \(\beta\) = 0.4 days\(^{-1}\), \(\gamma\) = 0.1 days \(^{-1}\) and the vaccine coverage p = 0.5
nicesubtitle <- "A simple model for vaccination v1, R0 of 4 and 50% vacc"
print(nicesubtitle)
## [1] "A simple model for vaccination v1, R0 of 4 and 50% vacc"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
N <- 1000000
duration <- 365 * 2
tsteps <- 1
beta <- 0.4
gamma <- 0.1
pvacc <- .50
R0 <- beta / gamma
# Reff threshold calc
pvacc_thresh <- 1 - (1/R0)
initial_state_values <- c(S = (N-1) * (1 - pvacc),
I = 1,
R = (N-1) * pvacc )
(parameters <- c(
beta = beta,
gamma = gamma,
R0 = R0,
pvacc = pvacc,
pvacc_thresh = pvacc_thresh
))
## beta gamma R0 pvacc pvacc_thresh
## 0.40 0.10 4.00 0.50 0.75
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S)
dI <- (lambda * S) -(gamma * I)
dR <- (gamma * I)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 130 248893.4 76711.5 674395.1 24.889 7.671 67.44 0.9955737
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 130 248893.4 76711.5 674395.1 24.889 7.671 67.44 0.9955737
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 731 730 101593.4 8.665453e-11 898406.6 10.159 0 89.841 0.4063737
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n p vacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n p vacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle)
Hopefully, you have seen that once a certain proportion of the population is immune, no epidemic occurs. This is called the critical vaccination threshold or herd immunity threshold.
nicesubtitle <- "A simple model for vaccination v2, R0 of 4 and calc vacc threshold"
print(nicesubtitle)
## [1] "A simple model for vaccination v2, R0 of 4 and calc vacc threshold"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
N <- 1000000
duration <- 365 * 2
tsteps <- 1
beta <- 0.4
gamma <- 0.1
pvacc <- .50
R0 <- beta / gamma
# Reff threshold calc
pvacc_thresh <- 1 - (1/R0)
# override pvacc with pvacc_thresh
pvacc <- pvacc_thresh
initial_state_values <- c(S = (N-1) * (1 - pvacc),
I = 1,
R = (N-1) * pvacc )
(parameters <- c(
beta = beta,
gamma = gamma,
R0 = R0,
pvacc = pvacc,
pvacc_thresh = pvacc_thresh
))
## beta gamma R0 pvacc pvacc_thresh
## 0.40 0.10 4.00 0.75 0.75
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S)
dI <- (lambda * S) -(gamma * I)
dR <- (gamma * I)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 0 249999.8 1 749999.2 25 0 75 0.999999
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 0 249999.8 1 749999.2 25 0 75 0.999999
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 731 730 249927 0.9893463 750072 24.993 0 75.007 0.9997081
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n p vacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n p vacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle)
Investigate how the herd immunity threshold changes if we are modelling a disease with a different infection and recovery rate.
if beta = 0.4 and gamma = 0.2 days\(^{-1}\), trying 10% vaccination rate (v3), then critical vaccination threshold of 83% afterwards.
No, it is not the case every time that everyone needs to be vaccinated to prevent an outbreak. In the prior scenario, critical vaccination threshold of .75 was enough. For the following example, the cvt is about .83.
nicesubtitle <- "A simple model for vaccination v3, lower R0 and 10% vacc"
print(nicesubtitle)
## [1] "A simple model for vaccination v3, lower R0 and 10% vacc"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
N <- 1000000
duration <- 365 * 2
tsteps <- 1
beta <- 0.4
gamma <- 0.2
pvacc <- .10 # try 10 percent
R0 <- beta / gamma
# Reff threshold calc
pvacc_thresh <- 1 - (1/R0)
initial_state_values <- c(S = (N-1) * (1 - pvacc),
I = 1,
R = (N-1) * pvacc )
(parameters <- c(
beta = beta,
gamma = gamma,
R0 = R0,
pvacc = pvacc,
pvacc_thresh = pvacc_thresh
))
## beta gamma R0 pvacc pvacc_thresh
## 0.4 0.2 2.0 0.1 0.5
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S)
dI <- (lambda * S) -(gamma * I)
dR <- (gamma * I)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 83 496363.7 106094 397542.3 49.636 10.609 39.754 0.9927275
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 83 496363.7 106094 397542.3 49.636 10.609 39.754 0.9927275
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 731 730 240812.5 1.350074e-23 759187.5 24.081 0 75.919 0.481625
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n p vacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n p vacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle)
Use critical vaccination threshold:
nicesubtitle <- "A simple model for vaccination v4, lower R0 and calc vacc threshold"
print(nicesubtitle)
## [1] "A simple model for vaccination v4, lower R0 and calc vacc threshold"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
N <- 1000000
duration <- 365 * 2
tsteps <- 1
beta <- 0.4
gamma <- 0.2
pvacc <- .0 # to be overwritten below with calculated threshold
R0 <- beta / gamma
# Reff threshold calc
pvacc_thresh <- 1 - (1/R0)
# override pvacc with pvacc_thresh
pvacc <- pvacc_thresh
initial_state_values <- c(S = (N-1) * (1 - pvacc),
I = 1,
R = (N-1) * pvacc )
(parameters <- c(
beta = beta,
gamma = gamma,
R0 = R0,
pvacc = pvacc,
pvacc_thresh = pvacc_thresh
))
## beta gamma R0 pvacc pvacc_thresh
## 0.4 0.2 2.0 0.5 0.5
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S)
dI <- (lambda * S) -(gamma * I)
dR <- (gamma * I)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 0 499999.5 1 499999.5 50 0 50 0.999999
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 0 499999.5 1 499999.5 50 0 50 0.999999
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 731 730 499854.6 0.9788434 500144.5 49.985 0 50.014 0.9997091
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n p vacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n p vacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle)
Changed parameters using 10% vaccination rate –>
- if beta = 0.6 and gamma = 0.1 days\(^{-1}\):
nicesubtitle <- "A simple model for vaccination v5, higher R0 and 10% vacc"
print(nicesubtitle)
## [1] "A simple model for vaccination v5, higher R0 and 10% vacc"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
N <- 1000000
duration <- 365 * 2
tsteps <- 1
beta <- 0.6
gamma <- 0.1
pvacc <- .10 # try 10 percent
R0 <- beta / gamma
# Reff threshold calc
pvacc_thresh <- 1 - (1/R0)
initial_state_values <- c(S = (N-1) * (1 - pvacc),
I = 1,
R = (N-1) * pvacc )
(parameters <- c(
beta = beta,
gamma = gamma,
R0 = R0,
pvacc = pvacc,
pvacc_thresh = pvacc_thresh
))
## beta gamma R0 pvacc pvacc_thresh
## 0.6000000 0.1000000 6.0000000 0.1000000 0.8333333
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S)
dI <- (lambda * S) -(gamma * I)
dR <- (gamma * I)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 35 163728.4 452241 384030.6 16.373 45.224 38.403 0.9823704
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 35 163728.4 452241 384030.6 16.373 45.224 38.403 0.9823704
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re
## 731 730 4167.877 1.352032e-23 995832.1 0.417 0 99.583
## Reff
## 731 0.02500726
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n p vacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n p vacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle)
Changed parameters using critical vaccination threshold –>
- if beta = 0.6 and gamma = 0.1 days\(^{-1}\):
nicesubtitle <- "A simple model for vaccination v6, higher R0 and calc vacc threshold"
print(nicesubtitle)
## [1] "A simple model for vaccination v6, higher R0 and calc vacc threshold"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
N <- 1000000
duration <- 365 * 2
tsteps <- 1
beta <- 0.6
gamma <- 0.1
pvacc <- .0 # to be overwritten below with calculated threshold
R0 <- beta / gamma
# Reff threshold calc
pvacc_thresh <- 1 - (1/R0)
# override pvacc with pvacc_thresh
pvacc <- pvacc_thresh
initial_state_values <- c(S = (N-1) * (1 - pvacc),
I = 1,
R = (N-1) * pvacc )
(parameters <- c(
beta = beta,
gamma = gamma,
R0 = R0,
pvacc = pvacc,
pvacc_thresh = pvacc_thresh
))
## beta gamma R0 pvacc pvacc_thresh
## 0.6000000 0.1000000 6.0000000 0.8333333 0.8333333
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S)
dI <- (lambda * S) -(gamma * I)
dR <- (gamma * I)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 0 166666.5 1 833332.5 16.667 0 83.333 0.999999
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 0 166666.5 1 833332.5 16.667 0 83.333 0.999999
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 731 730 166593.9 0.9841127 833405.1 16.659 0 83.341 0.9995634
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n p vacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n p vacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle)
As you can see, the proportion of the population that needs to be vaccinated varies with different infection-related parameters. Think about why that is so in the context of herd immunity, and what is different between the scenarios you have just modelled.
“In mathematical modelling terms, herd immunity is just the same as saying that Reff < 1. We can derive the herd immunity threshold by solving the formula for Reff for p when Reff = 1:”
\[\begin{align} R_{eff} & = R_{0} \frac{S}{N} \\ R_{eff} & = R_{0} (1-p) \\ p = 1-\frac{1}{R_{0}} \end{align}\]
“Remember, we can calculate R0 by dividing \(\beta\) by \(\gamma\).”
# this is where we get the following formula
pvacc_threshold <- 1 - (1/R0)
A neonatal vaccine has recently become available against an endemic viral infection in livestock. The Ministry of Agriculture has asked you to model the impact of different vaccination scenarios to inform the design of a farm animal vaccination programme. They provide you with the following information:
The SIR structure needs to be extended to incorporate vaccinated births (going into the R compartment), unvaccinated births (going into the S compartment), deaths, and waning immunity. As we are modelling an endemic infection, the initial conditions for the population don’t matter as long as they add up to 300000 according to the instructions (this is because all initial conditions will end up at the same endemic equilibrium, given enough time). For the baseline scenario, we are assuming no vaccine coverage (p_vacc = 0) and no waning of immunity (\(\sigma\) = 0).
nicesubtitle <- "Neonatal livestock vaccination v2 run for baseline "
print(nicesubtitle)
## [1] "Neonatal livestock vaccination v2 run for baseline "
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
N <- 300000 # population size
# modelling endemic infection - initial conditions for population won't matter
endemic_I <- round(N * .1) # assume 1% "small" endemic infected
endemic_R <- round(N * .6) # assume 39% endemic recovered
duration <- 365 * 6 # 3 year life span so track for 6 years
tsteps <- 2 # chunk into 2 day intervals
beta <- 1 # rate given as 1 day^-1
gamma <- 1/20 # 20 days
pvacc <- 0 # assume no vaccine coverage
sigma <- 0 # no waning immunity
mu <- 1/(3 * 365) # 3 year life span
birth <- mu # stable population so set to same as mu
R0 <- beta / gamma
pvacc_thresh <- 1 - (1/R0)
initial_state_values <- c(S = (N-(endemic_I + endemic_R)),
I = endemic_I,
R = endemic_R)
(parameters <- c(
beta = beta, # infection rate
gamma = gamma, # recovery rate
R0 = R0, # basic reproduction number
pvacc_thresh = pvacc_thresh, # vaccination threshold
mu = mu, # background mortality rate
birth = birth, # birth rate
pvacc = pvacc, # vaccine rate
sigma = sigma # immunity waning rate
))
## beta gamma R0 pvacc_thresh mu birth
## 1.00000e+00 5.00000e-02 2.00000e+01 9.50000e-01 9.13242e-04 9.13242e-04
## pvacc sigma
## 0.00000e+00 0.00000e+00
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S) -(S * mu) +(N * birth * (1-pvacc)) +(sigma * R)
dI <- (lambda * S) -(gamma * I) -(I * mu)
dR <- (gamma * I) -(R * mu) +(N * birth * (pvacc)) -(sigma * R)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 10 11876.03 78307.91 209816.1 3.959 26.103 69.939 0.7917352
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 10 11876.03 78307.91 209816.1 3.959 26.103 69.939 0.7917352
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1096 2190 15273.97 5107.193 279618.8 5.091 1.702 93.206 1.018265
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle)
# uses values from output v2 run above
print("See equilibrium values assumed at 2 years: ")
## [1] "See equilibrium values assumed at 2 years: "
output[output$time == 365*2,]
## time S I R still_Su preval_Inf propor_Re Reff
## 366 730 15250.79 5122.388 279626.8 5.084 1.707 93.209 1.016719
print("See equilibrium values assumed at 3 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 3 years (+1 because step is 2): "
output[output$time == 365*3+1,]
## time S I R still_Su preval_Inf propor_Re Reff
## 549 1096 15275.12 5107.069 279617.8 5.092 1.702 93.206 1.018341
# save SIR values at 3 years for use as initial values in next run
endem_S <- round(output[output$time == 365*3+1,2])
endem_I <- round(output[output$time == 365*3+1,3])
endem_R <- round(output[output$time == 365*3+1,4])
endem_N <- endem_S + endem_I + endem_R
nicesubtitle <- "Neonatal livestock vaccination v3 reduce I by 50%"
print(nicesubtitle)
## [1] "Neonatal livestock vaccination v3 reduce I by 50%"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
N <- 300000 # population size
# modelling endemic infection - initial conditions for population won't matter
endemic_I <- endem_I # take from last run baseline at year 3
endemic_R <- endem_R # take from last run baseline at year 3
duration <- 365 * 6 # 3 year life span so track for 6 years
tsteps <- 2 # chunk into 2 day intervals
beta <- 1 # rate given as 1 day^-1
gamma <- 1/20 # 20 days
# trying to reduce prevalence from 1.7% down to 0.85% (half) via vaccination
pvacc <- 0.48 # 50% gets us 0.805%, so let us try 48% vaccination rate
sigma <- 0 # no waning immunity
mu <- 1/(3 * 365) # 3 year life span
birth <- mu # stable population so set to same as mu
R0 <- beta / gamma
pvacc_thresh <- 1 - (1/R0)
initial_state_values <- c(S = (N-(endemic_I + endemic_R)),
I = endemic_I,
R = endemic_R)
(parameters <- c(
beta = beta, # infection rate
gamma = gamma, # recovery rate
R0 = R0,
pvacc_thresh = pvacc_thresh, # vaccination threshold
mu = mu, # background mortality rate
birth = birth, # birth rate
pvacc = pvacc, # vaccine rate
sigma = sigma # immunity waning rate
))
## beta gamma R0 pvacc_thresh mu birth
## 1.00000e+00 5.00000e-02 2.00000e+01 9.50000e-01 9.13242e-04 9.13242e-04
## pvacc sigma
## 4.80000e-01 0.00000e+00
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S) -(S * mu) +(N * birth * (1-pvacc)) +(sigma * R)
dI <- (lambda * S) -(gamma * I) -(I * mu)
dR <- (gamma * I) -(R * mu) +(N * birth * (pvacc)) -(sigma * R)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 0 15275 5107 279618 5.092 1.702 93.206 1.018333
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 4 14768.5 5089.632 280141.9 4.923 1.697 93.381 0.9845667
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1096 2190 15273.94 2524.305 282201.8 5.091 0.841 94.067 1.018263
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle)
print("See equilibrium values assumed at 3 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 3 years (+1 because step is 2): "
output[output$time == 365*3+1,]
## time S I R still_Su preval_Inf propor_Re Reff
## 549 1096 15276.8 2512.547 282210.7 5.092 0.838 94.07 1.018453
print("See equilibrium values assumed at 5 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 5 years (+1 because step is 2): "
output[output$time == 365*5+1,]
## time S I R still_Su preval_Inf propor_Re Reff
## 914 1826 15274.72 2524.406 282200.9 5.092 0.841 94.067 1.018315
nicesubtitle <- "Neonatal livestock vaccination v4 48% vacc, no wane"
print(nicesubtitle)
## [1] "Neonatal livestock vaccination v4 48% vacc, no wane"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
N <- 300000 # population size
# modelling endemic infection - initial conditions for population won't matter
endemic_I <- endem_I # take from last run baseline at year 3
endemic_R <- endem_R # take from last run baseline at year 3
duration <- 365 * 6 # 3 year life span so track for 6 years
tsteps <- 2 # chunk into 2 day intervals
beta <- 1 # rate given as 1 day^-1
gamma <- 1/20 # 20 days
# trying to reduce prevalence from 1.7% down to 0.85% (half) via vaccination
pvacc <- 0.48 # placeholder, see below for threshold overwrite
sigma <- 0 # no waning immunity
mu <- 1/(3 * 365) # 3 year life span
birth <- mu # stable population so set to same as mu
R0 <- beta / gamma
pvacc_thresh <- 1 - (1/R0)
pvacc <- pvacc_thresh # overwrite in attempt to eliminate
initial_state_values <- c(S = (N-(endemic_I + endemic_R)),
I = endemic_I,
R = endemic_R)
(parameters <- c(
beta = beta, # infection rate
gamma = gamma, # recovery rate
R0 = R0,
pvacc_thresh = pvacc_thresh, # vaccination threshold
mu = mu, # background mortality rate
birth = birth, # birth rate
pvacc = pvacc, # vaccine rate
sigma = sigma # immunity waning rate
))
## beta gamma R0 pvacc_thresh mu birth
## 1.00000e+00 5.00000e-02 2.00000e+01 9.50000e-01 9.13242e-04 9.13242e-04
## pvacc sigma
## 9.50000e-01 0.00000e+00
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S) -(S * mu) +(N * birth * (1-pvacc)) +(sigma * R)
dI <- (lambda * S) -(gamma * I) -(I * mu)
dR <- (gamma * I) -(R * mu) +(N * birth * (pvacc)) -(sigma * R)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 0 15275 5107 279618 5.092 1.702 93.206 1.018333
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 2 14763.96 5098.295 280137.7 4.921 1.699 93.379 0.9842637
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1096 2190 13699.03 1.413955e-10 286301 4.566 0 95.434 0.9132689
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle)
print("See equilibrium values assumed at 3 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 3 years (+1 because step is 2): "
(out_v4_y3 <- output[output$time == 365*3+1,])
## time S I R still_Su preval_Inf propor_Re Reff
## 549 1096 11466.83 1.326657e-06 288533.2 3.822 0 96.178 0.7644556
print("See equilibrium values assumed at 5 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 5 years (+1 because step is 2): "
(out_v4_y5 <- output[output$time == 365*5+1,])
## time S I R still_Su preval_Inf propor_Re Reff
## 914 1826 13186.01 1.282425e-09 286814 4.395 0 95.605 0.8790674
nicesubtitle <- "Neonatal livestock vaccination v5 no vacc, 1 yr wane"
print(nicesubtitle)
## [1] "Neonatal livestock vaccination v5 no vacc, 1 yr wane"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
N <- 300000 # population size
# modelling endemic infection - initial conditions for population won't matter
endemic_I <- endem_I # take from last run baseline at year 3
endemic_R <- endem_R # take from last run baseline at year 3
duration <- 365 * 6 # 3 year life span so track for 6 years
tsteps <- 2 # chunk into 2 day intervals
beta <- 1 # rate given as 1 day^-1
gamma <- 1/20 # 20 days
# trying to reduce prevalence from 1.7% down to 0.85% (half) via vaccination
pvacc <- 0.48 # placeholder, see below for zero out overwrite
sigma <- 1/(1 * 365) # average duration of immunity 1 year
mu <- 1/(3 * 365) # 3 year life span
birth <- mu # stable population so set to same as mu
R0 <- beta / gamma
pvacc_thresh <- 1 - (1/R0)
pvacc <- 0 # no vacc scenario
initial_state_values <- c(S = (N-(endemic_I + endemic_R)),
I = endemic_I,
R = endemic_R)
(parameters <- c(
beta = beta, # infection rate
gamma = gamma, # recovery rate
R0 = R0,
pvacc_thresh = pvacc_thresh, # vaccination threshold
mu = mu, # background mortality rate
birth = birth, # birth rate
pvacc = pvacc, # vaccine rate
sigma = sigma # immunity waning rate
))
## beta gamma R0 pvacc_thresh mu birth
## 1.000000000 0.050000000 20.000000000 0.950000000 0.000913242 0.000913242
## pvacc sigma
## 0.000000000 0.002739726
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S) -(S * mu) +(N * birth * (1-pvacc)) +(sigma * R)
dI <- (lambda * S) -(gamma * I) -(I * mu)
dR <- (gamma * I) -(R * mu) +(N * birth * (pvacc)) -(sigma * R)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 64 15208.95 23433.61 261357.4 5.07 7.811 87.119 1.01393
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 66 14819.61 23392.2 261788.2 4.94 7.797 87.263 0.9879743
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1096 2190 15273.97 19385.6 265340.4 5.091 6.462 88.447 1.018265
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle)
print("See equilibrium values assumed at 3 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 3 years (+1 because step is 2): "
(out_v5_y3 <- output[output$time == 365*3+1,])
## time S I R still_Su preval_Inf propor_Re Reff
## 549 1096 15273.97 19385.6 265340.4 5.091 6.462 88.447 1.018265
print("See equilibrium values assumed at 5 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 5 years (+1 because step is 2): "
(out_v5_y5 <- output[output$time == 365*5+1,])
## time S I R still_Su preval_Inf propor_Re Reff
## 914 1826 15273.97 19385.6 265340.4 5.091 6.462 88.447 1.018265
nicesubtitle <- "Neonatal livestock vaccination v6 48% vacc, 1 yr wane"
print(nicesubtitle)
## [1] "Neonatal livestock vaccination v6 48% vacc, 1 yr wane"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
N <- 300000 # population size
# modelling endemic infection - initial conditions for population won't matter
endemic_I <- endem_I # take from last run baseline at year 3
endemic_R <- endem_R # take from last run baseline at year 3
duration <- 365 * 6 # 3 year life span so track for 6 years
tsteps <- 2 # chunk into 2 day intervals
beta <- 1 # rate given as 1 day^-1
gamma <- 1/20 # 20 days
# trying to reduce prevalence from 1.7% down to 0.85% (half) via vaccination
pvacc <- 0.48 # placeholder, see below for threshold overwrite
sigma <- 1/(1 * 365) # average duration of immunity 1 year
mu <- 1/(3 * 365) # 3 year life span
birth <- mu # stable population so set to same as mu
R0 <- beta / gamma
pvacc_thresh <- 1 - (1/R0)
pvacc <- pvacc_thresh # overwrite in attempt to eliminate
initial_state_values <- c(S = (N-(endemic_I + endemic_R)),
I = endemic_I,
R = endemic_R)
(parameters <- c(
beta = beta, # infection rate
gamma = gamma, # recovery rate
R0 = R0,
pvacc_thresh = pvacc_thresh, # vaccination threshold
mu = mu, # background mortality rate
birth = birth, # birth rate
pvacc = pvacc, # vaccine rate
sigma = sigma # immunity waning rate
))
## beta gamma R0 pvacc_thresh mu birth
## 1.000000000 0.050000000 20.000000000 0.950000000 0.000913242 0.000913242
## pvacc sigma
## 0.950000000 0.002739726
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S) -(S * mu) +(N * birth * (1-pvacc)) +(sigma * R)
dI <- (lambda * S) -(gamma * I) -(I * mu)
dR <- (gamma * I) -(R * mu) +(N * birth * (pvacc)) -(sigma * R)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 72 15190.56 17455.19 267354.2 5.064 5.818 89.118 1.012704
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 74 14906.54 17428.61 267664.8 4.969 5.81 89.222 0.9937694
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1096 2190 15273.97 14534.54 270191.5 5.091 4.845 90.064 1.018265
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle)
print("See equilibrium values assumed at 3 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 3 years (+1 because step is 2): "
(out_v6_y3 <- output[output$time == 365*3+1,])
## time S I R still_Su preval_Inf propor_Re Reff
## 549 1096 15273.97 14534.54 270191.5 5.091 4.845 90.064 1.018265
print("See equilibrium values assumed at 5 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 5 years (+1 because step is 2): "
(out_v6_y5 <- output[output$time == 365*5+1,])
## time S I R still_Su preval_Inf propor_Re Reff
## 914 1826 15273.97 14534.54 270191.5 5.091 4.845 90.064 1.018265
print("R0 of 20, 1-year waning immunity, no vacc rate, year 5")
## [1] "R0 of 20, 1-year waning immunity, no vacc rate, year 5"
out_v5_y5
## time S I R still_Su preval_Inf propor_Re Reff
## 914 1826 15273.97 19385.6 265340.4 5.091 6.462 88.447 1.018265
print("R0 of 20, 1-year waning immunity, a vacc rate of .95, year 5")
## [1] "R0 of 20, 1-year waning immunity, a vacc rate of .95, year 5"
out_v6_y5
## time S I R still_Su preval_Inf propor_Re Reff
## 914 1826 15273.97 14534.54 270191.5 5.091 4.845 90.064 1.018265
print("reduction in prevalence with 95% neonatal vaccination rate, year 5")
## [1] "reduction in prevalence with 95% neonatal vaccination rate, year 5"
1 - out_v6_y5[3] / out_v5_y5[3]
## I
## 914 0.2502406
nicesubtitle <- "Neonatal livestock vaccination v7 100% vacc, 1 yr wane"
print(nicesubtitle)
## [1] "Neonatal livestock vaccination v7 100% vacc, 1 yr wane"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
N <- 300000 # population size
# modelling endemic infection - initial conditions for population won't matter
endemic_I <- endem_I # take from last run baseline at year 3
endemic_R <- endem_R # take from last run baseline at year 3
duration <- 365 * 6 # 3 year life span so track for 6 years
tsteps <- 2 # chunk into 2 day intervals
beta <- 1 # rate given as 1 day^-1
gamma <- 1/20 # 20 days
# trying to reduce prevalence from 1.7% down to 0.85% (half) via vaccination
pvacc <- 1.0 # try 100% vacc rate and comment out threshold below
sigma <- 1/(1 * 365) # average duration of immunity 1 year
mu <- 1/(3 * 365) # 3 year life span
birth <- mu # stable population so set to same as mu
R0 <- beta / gamma
pvacc_thresh <- 1 - (1/R0)
# pvacc <- pvacc_thresh # no overwrite
initial_state_values <- c(S = (N-(endemic_I + endemic_R)),
I = endemic_I,
R = endemic_R)
(parameters <- c(
beta = beta, # infection rate
gamma = gamma, # recovery rate
R0 = R0,
pvacc_thresh = pvacc_thresh, # vaccination threshold
mu = mu, # background mortality rate
birth = birth, # birth rate
pvacc = pvacc, # vaccine rate
sigma = sigma # immunity waning rate
))
## beta gamma R0 pvacc_thresh mu birth
## 1.000000000 0.050000000 20.000000000 0.950000000 0.000913242 0.000913242
## pvacc sigma
## 1.000000000 0.002739726
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S) -(S * mu) +(N * birth * (1-pvacc)) +(sigma * R)
dI <- (lambda * S) -(gamma * I) -(I * mu)
dR <- (gamma * I) -(R * mu) +(N * birth * (pvacc)) -(sigma * R)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 72 15269.74 17133.96 267596.3 5.09 5.711 89.199 1.017982
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 74 14982.38 17116.72 267900.9 4.994 5.706 89.3 0.9988251
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1096 2190 15273.97 14279.22 270446.8 5.091 4.76 90.149 1.018265
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle)
print("See equilibrium values assumed at 3 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 3 years (+1 because step is 2): "
(out_v7_y3 <- output[output$time == 365*3+1,])
## time S I R still_Su preval_Inf propor_Re Reff
## 549 1096 15273.97 14279.22 270446.8 5.091 4.76 90.149 1.018265
print("See equilibrium values assumed at 5 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 5 years (+1 because step is 2): "
(out_v7_y5 <- output[output$time == 365*5+1,])
## time S I R still_Su preval_Inf propor_Re Reff
## 914 1826 15273.97 14279.22 270446.8 5.091 4.76 90.149 1.018265
nicesubtitle <- "Neonatal livestock vaccination v8 no vacc, 2.5 yr wane"
print(nicesubtitle)
## [1] "Neonatal livestock vaccination v8 no vacc, 2.5 yr wane"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
N <- 300000 # population size
# modelling endemic infection - initial conditions for population won't matter
endemic_I <- endem_I # take from last run baseline at year 3
endemic_R <- endem_R # take from last run baseline at year 3
duration <- 365 * 6 # 3 year life span so track for 6 years
tsteps <- 2 # chunk into 2 day intervals
beta <- 1 # rate given as 1 day^-1
gamma <- 1/20 # 20 days
# trying to reduce prevalence from 1.7% down to 0.85% (half) via vaccination
pvacc <- 0.0 # no vacc, no threshold overwrite below
sigma <- 1/(2.5 * 365) # average duration of immunity 2.5 years
mu <- 1/(3 * 365) # 3 year life span
birth <- mu # stable population so set to same as mu
R0 <- beta / gamma
pvacc_thresh <- 1 - (1/R0)
# pvacc <- pvacc_thresh # no overwrite
initial_state_values <- c(S = (N-(endemic_I + endemic_R)),
I = endemic_I,
R = endemic_R)
(parameters <- c(
beta = beta, # infection rate
gamma = gamma, # recovery rate
R0 = R0,
pvacc_thresh = pvacc_thresh, # vaccination threshold
mu = mu, # background mortality rate
birth = birth, # birth rate
pvacc = pvacc, # vaccine rate
sigma = sigma # immunity waning rate
))
## beta gamma R0 pvacc_thresh mu birth
## 1.00000e+00 5.00000e-02 2.00000e+01 9.50000e-01 9.13242e-04 9.13242e-04
## pvacc sigma
## 0.00000e+00 1.09589e-03
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S) -(S * mu) +(N * birth * (1-pvacc)) +(sigma * R)
dI <- (lambda * S) -(gamma * I) -(I * mu)
dR <- (gamma * I) -(R * mu) +(N * birth * (pvacc)) -(sigma * R)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 80 15331.97 13028.96 271639.1 5.111 4.343 90.546 1.022131
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 84 14937.56 13003.69 272058.7 4.979 4.335 90.686 0.9958376
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1096 2190 15273.97 10999.07 273727 5.091 3.666 91.242 1.018265
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle)
print("See equilibrium values assumed at 3 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 3 years (+1 because step is 2): "
(out_v8_y3 <- output[output$time == 365*3+1,])
## time S I R still_Su preval_Inf propor_Re Reff
## 549 1096 15273.97 10999.07 273727 5.091 3.666 91.242 1.018265
print("See equilibrium values assumed at 5 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 5 years (+1 because step is 2): "
(out_v8_y5 <- output[output$time == 365*5+1,])
## time S I R still_Su preval_Inf propor_Re Reff
## 914 1826 15273.97 10999.07 273727 5.091 3.666 91.242 1.018265
nicesubtitle <- "Neonatal livestock vaccination v9 100% vacc, 2.5 yr wane"
print(nicesubtitle)
## [1] "Neonatal livestock vaccination v9 100% vacc, 2.5 yr wane"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
N <- 300000 # population size
# modelling endemic infection - initial conditions for population won't matter
endemic_I <- endem_I # take from last run baseline at year 3
endemic_R <- endem_R # take from last run baseline at year 3
duration <- 365 * 6 # 3 year life span so track for 6 years
tsteps <- 2 # chunk into 2 day intervals
beta <- 1 # rate given as 1 day^-1
gamma <- 1/20 # 20 days
# trying to reduce prevalence from 1.7% down to 0.85% (half) via vaccination
pvacc <- 1.0 # 100% vacc, no threshold overwrite below
sigma <- 1/(2.5 * 365) # average duration of immunity 2.5 years
mu <- 1/(3 * 365) # 3 year life span
birth <- mu # stable population so set to same as mu
R0 <- beta / gamma
pvacc_thresh <- 1 - (1/R0)
# pvacc <- pvacc_thresh # no overwrite
initial_state_values <- c(S = (N-(endemic_I + endemic_R)),
I = endemic_I,
R = endemic_R)
(parameters <- c(
beta = beta, # infection rate
gamma = gamma, # recovery rate
R0 = R0,
pvacc_thresh = pvacc_thresh, # vaccination threshold
mu = mu, # background mortality rate
birth = birth, # birth rate
pvacc = pvacc, # vaccine rate
sigma = sigma # immunity waning rate
))
## beta gamma R0 pvacc_thresh mu birth
## 1.00000e+00 5.00000e-02 2.00000e+01 9.50000e-01 9.13242e-04 9.13242e-04
## pvacc sigma
## 1.00000e+00 1.09589e-03
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S) -(S * mu) +(N * birth * (1-pvacc)) +(sigma * R)
dI <- (lambda * S) -(gamma * I) -(I * mu)
dR <- (gamma * I) -(R * mu) +(N * birth * (pvacc)) -(sigma * R)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 106 15273.5 5950.337 278776.2 5.091 1.983 92.925 1.018233
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## [1] time S I R still_Su preval_Inf propor_Re
## [8] Reff
## <0 rows> (or 0-length row.names)
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1096 2190 15273.97 5731.295 278994.7 5.091 1.91 92.998 1.018265
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle)
print("See equilibrium values assumed at 3 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 3 years (+1 because step is 2): "
(out_v9_y3 <- output[output$time == 365*3+1,])
## time S I R still_Su preval_Inf propor_Re Reff
## 549 1096 15273.98 5731.291 278994.7 5.091 1.91 92.998 1.018265
print("See equilibrium values assumed at 5 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 5 years (+1 because step is 2): "
(out_v9_y5 <- output[output$time == 365*5+1,])
## time S I R still_Su preval_Inf propor_Re Reff
## 914 1826 15273.97 5731.295 278994.7 5.091 1.91 92.998 1.018265
print("R0 of 20, 2.5-year waning immunity, no vacc rate, year 5")
## [1] "R0 of 20, 2.5-year waning immunity, no vacc rate, year 5"
out_v8_y5
## time S I R still_Su preval_Inf propor_Re Reff
## 914 1826 15273.97 10999.07 273727 5.091 3.666 91.242 1.018265
print("R0 of 20, 2.5-year waning immunity, a 100% vacc rate, year 5")
## [1] "R0 of 20, 2.5-year waning immunity, a 100% vacc rate, year 5"
out_v9_y5
## time S I R still_Su preval_Inf propor_Re Reff
## 914 1826 15273.97 5731.295 278994.7 5.091 1.91 92.998 1.018265
print("reduction in prevalence with 95% neonatal vaccination rate, year 5")
## [1] "reduction in prevalence with 95% neonatal vaccination rate, year 5"
1 - out_v9_y5[3] / out_v8_y5[3]
## I
## 914 0.4789293
We saw that with a 1-year waning immunity, no vaccination results in a baseline prevalence of 6.5%. Using the vaccine adjuvant, we now have a 2.5-year waning immunity; with no vaccination the baseline prevalence drops to 3.7% (43% drop). Adding 100% vaccination results in a baseline prevalence of 1.9%, which appears to be the best scenario we’ve tested so far.
Assuming safety studies are mature and acceptable, I recommend neonatal vaccination ideally at 100% along with the vaccine adjuvant while continuing to set up parallel studies that can track other interventions that could help drive the prevalence further down. Also, long term studies could provide more insight.
We still need more research and studies to determine other interventions. Even a hypothetical magic adjuvant that would extend immunity to 12 years will still result in about 0.3% prevalence at 5 years, which is not zero.
nicesubtitle <- "Neonatal livestock vaccination v10 100% vacc, 12 yr wane"
print(nicesubtitle)
## [1] "Neonatal livestock vaccination v10 100% vacc, 12 yr wane"
print("initial state values and parameters")
## [1] "initial state values and parameters"
# MODEL INPUTS:
N <- 300000 # population size
# modelling endemic infection - initial conditions for population won't matter
endemic_I <- endem_I # take from last run baseline at year 3
endemic_R <- endem_R # take from last run baseline at year 3
duration <- 365 * 6 # 3 year life span so track for 6 years
tsteps <- 2 # chunk into 2 day intervals
beta <- 1 # rate given as 1 day^-1
gamma <- 1/20 # 20 days
# trying to reduce prevalence from 1.7% down to 0.85% (half) via vaccination
pvacc <- 1.0 # 100% vacc, no threshold overwrite below
sigma <- 1/(12 * 365) # assume magic adjuvant for average duration of immunity 12 years
mu <- 1/(3 * 365) # 3 year life span
birth <- mu # stable population so set to same as mu
R0 <- beta / gamma
pvacc_thresh <- 1 - (1/R0)
# pvacc <- pvacc_thresh # no overwrite
initial_state_values <- c(S = (N-(endemic_I + endemic_R)),
I = endemic_I,
R = endemic_R)
(parameters <- c(
beta = beta, # infection rate
gamma = gamma, # recovery rate
R0 = R0,
pvacc_thresh = pvacc_thresh, # vaccination threshold
mu = mu, # background mortality rate
birth = birth, # birth rate
pvacc = pvacc, # vaccine rate
sigma = sigma # immunity waning rate
))
## beta gamma R0 pvacc_thresh mu birth
## 1.000000e+00 5.000000e-02 2.000000e+01 9.500000e-01 9.132420e-04 9.132420e-04
## pvacc sigma
## 1.000000e+00 2.283105e-04
# TIMESTEPS:
times <- seq(from = 0, to = duration, by = tsteps)
# SIR MODEL FUNCTION
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
N <- S + I + R
lambda <- beta * I/N
dS <- -(lambda * S) -(S * mu) +(N * birth * (1-pvacc)) +(sigma * R)
dI <- (lambda * S) -(gamma * I) -(I * mu)
dR <- (gamma * I) -(R * mu) +(N * birth * (pvacc)) -(sigma * R)
return(list(c(dS, dI, dR)))
})
}
# MODEL OUTPUT (solving the differential equations):
# tidyverse pipe to add fields including Reff
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters)) %>%
mutate(still_Su = round(S/(S + I + R),digits=5)*100,
preval_Inf = round(I/(S+ I + R),digits=5)*100,
propor_Re = round(R/(S + I + R),digits=5)*100,
Reff = (parameters['beta']/parameters['gamma'])
* (S/(S + I + R)) )
print("peak infection day when I is at its max: ")
## [1] "peak infection day when I is at its max: "
output %>%
arrange(-I, time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 0 15275 5107 279618 5.092 1.702 93.206 1.018333
print("point when R eff goes below 1: ")
## [1] "point when R eff goes below 1: "
output %>%
filter(Reff < 1) %>%
arrange(time) %>%
head(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1 2 14862.5 5099.979 280037.5 4.954 1.7 93.346 0.9908334
print("last record for the run: ")
## [1] "last record for the run: "
output %>%
arrange(time) %>%
tail(1)
## time S I R still_Su preval_Inf propor_Re Reff
## 1096 2190 15246.59 985.4795 283767.9 5.082 0.328 94.589 1.016439
print("Plotting the proportion of people in each compartment over time")
## [1] "Plotting the proportion of people in each compartment over time"
output %>%
select(-still_Su, -preval_Inf, -propor_Re, -Reff) %>%
melt(id = "time") %>%
mutate(proportion = value / sum(initial_state_values)) %>%
ggplot(aes(x=time, y=proportion, color=variable, shape=variable)) +
geom_line() +
geom_jitter(show.legend = FALSE) +
scale_color_manual(values = c("blue","red","green")) +
scale_shape_manual(values = c(0,4,1)) +
xlab("Time (days)") +
ylab("Proportion of people") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle) +
theme(legend.position="bottom")
print("Plotting R eff")
## [1] "Plotting R eff"
output %>%
select(time, Reff) %>%
ggplot(aes(x=time, y=Reff)) +
geom_line(linetype="dotdash", color="orange") +
geom_jitter(color="orange", shape=6, show.legend = FALSE) +
xlab("Time (days)") +
ylab("R Effective") +
labs(title=paste(" beta of", parameters['beta'],
" gamma of", parameters['gamma'],
" = R0 of", parameters['R0'],
" \n mu of", round(parameters['mu'],5),"/ b of", round(parameters['birth'],5)," / step ", tsteps,
" \n pvacc of", parameters['pvacc']),
color = "Compartment",
subtitle= nicesubtitle)
print("See equilibrium values assumed at 3 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 3 years (+1 because step is 2): "
(out_v10_y3 <- output[output$time == 365*3+1,])
## time S I R still_Su preval_Inf propor_Re Reff
## 549 1096 14600.78 1088.564 284310.7 4.867 0.363 94.77 0.9733851
print("See equilibrium values assumed at 5 years (+1 because step is 2): ")
## [1] "See equilibrium values assumed at 5 years (+1 because step is 2): "
(out_v10_y5 <- output[output$time == 365*5+1,])
## time S I R still_Su preval_Inf propor_Re Reff
## 914 1826 15402.47 978.0193 283619.5 5.134 0.326 94.54 1.026831