This tutorial simulates a simple population model where mortality is the only process. The differential equation version of the model is:
$$ {dN \over dt} = - \mu N $$
where $N$ is the population size as a function of time $t$ and $\mu$ is the mortality rate.
This R code defines a function that calculates the population over a time range, using the analytical solution to the differential equation:
analytic <- function( initial = initialPopulationSize, rate = mortalityRate, stepSize = timeStep, maxT = maxTime ) { times = seq(0, maxT, stepSize) ts <- data.frame( time = times, populationSize = initial * exp(-rate*times) ) return(ts) }
This R code defines a function that calculates the population over a time range, using a discrete time approximation to the differential equation:
discreteTime <- function( initial = initialPopulationSize, rate = mortalityRate, stepSize = timeStep, maxT = maxTime ) { tt <- 0 pop <- initial ts <- data.frame(time = tt, populationSize = pop) while (tt<maxT){ nextt <- tt + stepSize nextpop <- pop - rate*pop*stepSize ts <- rbind( ts, c(time = nextt, populationSize = nextpop) ) tt <- nextt pop <- nextpop } return(ts) }
This R code defines a function that calculates the population over a time range, using a stochastic simulation of the process represented by the differential equation model:
individual <- function( initial = initialPopulationSize, rate = mortalityRate, stepSize = timeStep, maxT = maxTime ) { deathTimes <- rexp(initial, rate) ts <- data.frame(time = 0, populationSize = initial) for(tt in seq(stepSize, maxT, stepSize)){ ts <- rbind( ts, c(time = tt, populationSize = sum(deathTimes >= tt))) } return(ts) }
Set the parameter values
initialPopulationSize <- 10 # number of individuals mortalityRate <- 0.05 # per capital deaths per day timeStep <- 1 # days maxTime <- 30 # days
Setup plotting
par(bty='L',lwd=3,mar=c(4,4,1,1)) plot(NA,NA,ylim=c(0,initialPopulationSize),xlim=c(0,maxTime), ylab='Population size',xlab='Time') plotter <- function(ts, col) with(ts, lines(time, populationSize, col=col))
Now run the three functions to see what you get
plotter(analytic(), col="black") plotter(discreteTime(), col="green") plotter(individual(), col="red")
Try running these three lines multiple times without resetting the parameters or re-making the plot. Which functions give you different outcomes each time?
Now try changing the parameter values above and re-running the functions (you may want to re-make the plot as well). How does changing each of the values change the output? Can you get the greencurve to diverge from the black curve? How?
Compartments: (S,I,R) = (susceptible, infectious, removed)
| Event | Change | Rate | |:---------------------:|:-------------------------:|:-----------:| | Spillover (S) | (S, I, R)->(S-1, I+1, R) | $\lambda$ | | Infection (S) | (S, I, R)->(S-1, I+1, R) | $\beta I S$ | | Recovery/Removal (I) | (S, I, R)->(S, I-1, R+1) | $\gamma I$ |
transitions <- list( spillover = c(S = -1, I = 1, R = 0), infection = c(S = -1, I = 1, R = 0), recovery = c(S = 0, I = -1, R = 1), end = c(S = 0, I = 0, R = 0) )
N=50 # population size parms=list( lambda=.02, # spillover rate beta=.2, # contact rate gamma=.1 # recovery rate )
event <- function(prevevent, params){ with(c(prevevent, params),{ # update rates rates <- c( spillover = ifelse(S>0, lambda, 0), # no spillover infections if S depleted infect = beta*I*S/N, recover = gamma*I ) totRate <- sum(rates) # if the event rate has gone to 0, skip to end of simulation if (totRate==0) { eventTime <- final.time eventType <- "end" } else { # calculate time of the next event eventTime <- time + rexp(1, totRate) # choose type of event eventType <- sample( c("spillover","infection","recovery"), size=1, replace=F, prob=rates/totRate ) } # determine changes based on the event type update <- transitions[[eventType]] return(data.frame( time = eventTime, as.list(update + c(S, I, R)), outcome = eventType )) }) }
simulateSIR <- function(t,y,params){ with(as.list(y),{ ts <- data.frame( time=0, S=round(S), I=round(I), R=round(R), outcome = "spillover" ) nextEvent <- ts while(nextEvent$time < final.time){ nextEvent <- event(nextEvent,params) ts <- rbind(ts, nextEvent) } return(ts) }) }
final.time=400 parms=parms # run the simulation tsTest <- simulateSIR(final.time, c(S=N-1,I=1,R=0), parms) # plot the infectious individuals over time plot( tsTest$time, tsTest$I, type='s', main="Number Infected", ylim=c(0,N), xlim=c(0,final.time), xlab="Time", ylab="", bty="n", cex.main=2, cex.lab=1.5, cex.axis=1.25, lwd=2 ) # plot all compartments over time cols <- c(S="black",I="red2",R="purple") plot( tsTest$time, tsTest$S, type='s', ylim=c(0,N+1), bty="n", ylab="Number of individuals", xlab="time", lwd=2, col = cols["S"] ) lines(tsTest$time, tsTest$I, type='s', col = cols["I"], lwd = 2) lines(tsTest$time, tsTest$R, type='s', col = cols["R"], lwd = 2) legend( x = "right", names(cols), lty = 1, col = cols, bty = "n", lwd = 2 ) # print the total number of infections sum(tsTest$outcome == "infection") # print the number of spillover events sum(tsTest$outcome == "spillover") - 1
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.