LOAD THE PACKAGES:
library(deSolve)
library(reshape2)
library(ggplot2)
SIR MODEL FUNCTION:
# The model function takes as input arguments (in the following order): time, state and parameters
sir_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), { # tell R to unpack variable names from the state and parameters inputs
# Calculating the total population size N (the sum of the number of people in each compartment)
N <- S+I+R
# 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)))
})
}
MODEL OUTPUT (solving the differential equations):
# Solving the differential equations using the ode integration algorithm
output <- as.data.frame(ode(y = initial_state_values,
times = times,
func = sir_model,
parms = parameters))
Generating 2 plots:
output_long <- melt(as.data.frame(output), id = "time") # turn output dataset into long format
Plot the proportion of people in the S, I and R compartments over time
ggplot(data = output_long, # specify object containing data to plot
aes(x = time, y = proportion, colour = variable, group = variable)) + # assign columns to axes and groups
geom_line() + # represent data as lines
xlab("Time (days)")+ # add label for x axis
ylab("Proportion of the population") + # add label for y axis
labs(colour = "Compartment", # add legend title
title = "Proportion susceptible, infected and recovered over time") +
theme(legend.position = "bottom") # move legend to the bottom of the plot

Calculating the effective reproduction number in a new column
output$reff <- parameters["beta"]/parameters["gamma"] * # R0 = beta/gamma
output$S/(output$S+output$I+output$R) # multiply R0 by the proportion susceptible
# at each timestep/for each row
# In this calculation, the total population size (output$S+output$I+output$R) is calculated for each timestep
# so this approach would also be appropriate if the population size varies over time
Plot Reff
ggplot(data = output, # specify object containing data to plot
aes(x = time, y = reff)) + # assign columns to axes and groups
geom_line() + # represent data as lines
xlab("Time (days)")+ # add label for x axis
ylab("Reff") + # add label for y axis
labs(title = "Effective reproduction number over time") # add plot title

LS0tDQp0aXRsZTogIlNpbXVsYXRpbmcgdGhlIGVmZmVjdGl2ZSByZXByb2R1Y3Rpb24gbnVtYmVyIFJlZmYgIg0KYXV0aG9yOiAiQkluaCBUaGFuZyBUcmFuIg0KZGF0ZTogIjUvMjMvMjAyMCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiBqb3VybmFsDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICB3b3JkX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQotLS0NCg0KIyBMT0FEIFRIRSBQQUNLQUdFUzoNCg0KYGBge3J9DQpsaWJyYXJ5KGRlU29sdmUpDQpsaWJyYXJ5KHJlc2hhcGUyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KYGBgDQoNCiMgTU9ERUwgSU5QVVRTOg0KDQojIyBWZWN0b3Igc3RvcmluZyB0aGUgaW5pdGlhbCBudW1iZXIgb2YgcGVvcGxlIGluIGVhY2ggY29tcGFydG1lbnQgKGF0IHRpbWVzdGVwIDApDQoNCmBgYHtyfQ0KDQppbml0aWFsX3N0YXRlX3ZhbHVlcyA8LSBjKFMgPSAxMDAwMDAwLTEsICAgIyB0aGUgd2hvbGUgcG9wdWxhdGlvbiB3ZSBhcmUgbW9kZWxsaW5nIGlzIHN1c2NlcHRpYmxlIHRvIGluZmVjdGlvbg0KICAgICAgICAgICAgICAgICAgICAgICAgICBJID0gMSwgICAgICAgICAgICMgdGhlIGVwaWRlbWljIHN0YXJ0cyB3aXRoIGEgc2luZ2xlIGluZmVjdGVkIHBlcnNvbg0KICAgICAgICAgICAgICAgICAgICAgICAgICBSID0gMCkgICAgICAgICAgICMgdGhlcmUgaXMgbm8gcHJpb3IgaW1tdW5pdHkgaW4gdGhlIHBvcHVsYXRpb24NCg0KYGBgDQoNCiMjIFZlY3RvciBzdG9yaW5nIHRoZSBwYXJhbWV0ZXJzIGRlc2NyaWJpbmcgdGhlIHRyYW5zaXRpb24gcmF0ZXMgaW4gdW5pdHMgb2YgZGF5c14tMQ0KYGBge3J9DQoNCnBhcmFtZXRlcnMgPC0gYyhiZXRhID0gMC40LCAgICAgICMgdGhlIGluZmVjdGlvbiByYXRlLCB3aGljaCBhY3RzIG9uIHN1c2NlcHRpYmxlcw0KICAgICAgICAgICAgICAgIGdhbW1hID0gMC4xKSAgICAgIyB0aGUgcmF0ZSBvZiByZWNvdmVyeSwgd2hpY2ggYWN0cyBvbiB0aG9zZSBpbmZlY3RlZA0KDQoNCmBgYA0KDQojIyBWZWN0b3Igc3RvcmluZyB0aGUgc2VxdWVuY2Ugb2YgdGltZXN0ZXBzIHRvIHNvbHZlIHRoZSBtb2RlbCBhdA0KYGBge3J9DQojIFRJTUVTVEVQUzoNCg0KIyBWZWN0b3Igc3RvcmluZyB0aGUgc2VxdWVuY2Ugb2YgdGltZXN0ZXBzIHRvIHNvbHZlIHRoZSBtb2RlbCBhdA0KdGltZXMgPC0gc2VxKGZyb20gPSAwLCB0byA9IDEwMCwgYnkgPSAxKSAgICMgZnJvbSAwIHRvIDEwMCBkYXlzIGluIGRhaWx5IGludGVydmFscw0KYGBgDQoNCiMgU0lSIE1PREVMIEZVTkNUSU9OOiANCg0KYGBge3J9DQojIFRoZSBtb2RlbCBmdW5jdGlvbiB0YWtlcyBhcyBpbnB1dCBhcmd1bWVudHMgKGluIHRoZSBmb2xsb3dpbmcgb3JkZXIpOiB0aW1lLCBzdGF0ZSBhbmQgcGFyYW1ldGVycw0Kc2lyX21vZGVsIDwtIGZ1bmN0aW9uKHRpbWUsIHN0YXRlLCBwYXJhbWV0ZXJzKSB7ICANCg0KICAgIHdpdGgoYXMubGlzdChjKHN0YXRlLCBwYXJhbWV0ZXJzKSksIHsgICMgdGVsbCBSIHRvIHVucGFjayB2YXJpYWJsZSBuYW1lcyBmcm9tIHRoZSBzdGF0ZSBhbmQgcGFyYW1ldGVycyBpbnB1dHMgICAgDQogICAgICAgIA0KICAgICMgQ2FsY3VsYXRpbmcgdGhlIHRvdGFsIHBvcHVsYXRpb24gc2l6ZSBOICh0aGUgc3VtIG9mIHRoZSBudW1iZXIgb2YgcGVvcGxlIGluIGVhY2ggY29tcGFydG1lbnQpDQogICAgICBOIDwtIFMrSStSDQogICAgICANCiAgICAjIERlZmluaW5nIGxhbWJkYSBhcyBhIGZ1bmN0aW9uIG9mIGJldGEgYW5kIEk6DQogICAgICBsYW1iZGEgPC0gYmV0YSAqIEkvTg0KICAgICAgICANCiAgICAjIFRoZSBkaWZmZXJlbnRpYWwgZXF1YXRpb25zDQogICAgICBkUyA8LSAtbGFtYmRhICogUyAgICAgICAgICAgICAgICMgcGVvcGxlIG1vdmUgb3V0IG9mICgtKSB0aGUgUyBjb21wYXJ0bWVudCBhdCBhIHJhdGUgbGFtYmRhIChmb3JjZSBvZiBpbmZlY3Rpb24pDQogICAgICBkSSA8LSBsYW1iZGEgKiBTIC0gZ2FtbWEgKiBJICAgICMgcGVvcGxlIG1vdmUgaW50byAoKykgdGhlIEkgY29tcGFydG1lbnQgZnJvbSBTIGF0IGEgcmF0ZSBsYW1iZGEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGFuZCBtb3ZlIG91dCBvZiAoLSkgdGhlIEkgY29tcGFydG1lbnQgYXQgYSByYXRlIGdhbW1hIChyZWNvdmVyeSkNCiAgICAgIGRSIDwtIGdhbW1hICogSSAgICAgICAgICAgICAgICAgIyBwZW9wbGUgbW92ZSBpbnRvICgrKSB0aGUgUiBjb21wYXJ0bWVudCBmcm9tIEkgYXQgYSByYXRlIGdhbW1hDQogICAgICANCiAgICAjIFJldHVybiB0aGUgbnVtYmVyIG9mIHBlb3BsZSBpbiB0aGUgUywgSSBhbmQgUiBjb21wYXJ0bWVudHMgYXQgZWFjaCB0aW1lc3RlcCANCiAgICAjIChpbiB0aGUgc2FtZSBvcmRlciBhcyB0aGUgaW5wdXQgc3RhdGUgdmFyaWFibGVzKQ0KICAgIHJldHVybihsaXN0KGMoZFMsIGRJLCBkUikpKSANCiAgICB9KQ0KICANCn0NCmBgYA0KDQojIyBNT0RFTCBPVVRQVVQgKHNvbHZpbmcgdGhlIGRpZmZlcmVudGlhbCBlcXVhdGlvbnMpOg0KDQpgYGB7cn0NCiMgU29sdmluZyB0aGUgZGlmZmVyZW50aWFsIGVxdWF0aW9ucyB1c2luZyB0aGUgb2RlIGludGVncmF0aW9uIGFsZ29yaXRobQ0Kb3V0cHV0IDwtIGFzLmRhdGEuZnJhbWUob2RlKHkgPSBpbml0aWFsX3N0YXRlX3ZhbHVlcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZXMgPSB0aW1lcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuYyA9IHNpcl9tb2RlbCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJtcyA9IHBhcmFtZXRlcnMpKQ0KYGBgDQoNCg0KIyMgR2VuZXJhdGluZyAyIHBsb3RzOg0KDQpgYGB7cn0NCm91dHB1dF9sb25nIDwtIG1lbHQoYXMuZGF0YS5mcmFtZShvdXRwdXQpLCBpZCA9ICJ0aW1lIikgICAgICAgIyB0dXJuIG91dHB1dCBkYXRhc2V0IGludG8gbG9uZyBmb3JtYXQNCg0KYGBgDQoNCg0KIyMgQ2FsY3VsYXRpbmcgdGhlIHByb3BvcnRpb24gaW4gZWFjaCBjb21wYXJ0bWVudCBhcyBhIGNvbHVtbiBpbiB0aGUgbG9uZy1mb3JtYXQgb3V0cHV0DQoNCmBgYHtyfQ0Kb3V0cHV0X2xvbmckcHJvcG9ydGlvbiA8LSBvdXRwdXRfbG9uZyR2YWx1ZS9zdW0oaW5pdGlhbF9zdGF0ZV92YWx1ZXMpDQpgYGANCg0KDQojIyBQbG90IHRoZSBwcm9wb3J0aW9uIG9mIHBlb3BsZSBpbiB0aGUgUywgSSBhbmQgUiBjb21wYXJ0bWVudHMgb3ZlciB0aW1lDQpgYGB7cn0NCmdncGxvdChkYXRhID0gb3V0cHV0X2xvbmcsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHNwZWNpZnkgb2JqZWN0IGNvbnRhaW5pbmcgZGF0YSB0byBwbG90DQogICAgICAgYWVzKHggPSB0aW1lLCB5ID0gcHJvcG9ydGlvbiwgY29sb3VyID0gdmFyaWFibGUsIGdyb3VwID0gdmFyaWFibGUpKSArICAjIGFzc2lnbiBjb2x1bW5zIHRvIGF4ZXMgYW5kIGdyb3Vwcw0KICBnZW9tX2xpbmUoKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgcmVwcmVzZW50IGRhdGEgYXMgbGluZXMNCiAgeGxhYigiVGltZSAoZGF5cykiKSsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGFkZCBsYWJlbCBmb3IgeCBheGlzDQogIHlsYWIoIlByb3BvcnRpb24gb2YgdGhlIHBvcHVsYXRpb24iKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBhZGQgbGFiZWwgZm9yIHkgYXhpcw0KICBsYWJzKGNvbG91ciA9ICJDb21wYXJ0bWVudCIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgYWRkIGxlZ2VuZCB0aXRsZSAgDQogICAgICAgdGl0bGUgPSAiUHJvcG9ydGlvbiBzdXNjZXB0aWJsZSwgaW5mZWN0ZWQgYW5kIHJlY292ZXJlZCBvdmVyIHRpbWUiKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBtb3ZlIGxlZ2VuZCB0byB0aGUgYm90dG9tIG9mIHRoZSBwbG90DQpgYGANCg0KIyBDYWxjdWxhdGluZyB0aGUgZWZmZWN0aXZlIHJlcHJvZHVjdGlvbiBudW1iZXIgaW4gYSBuZXcgY29sdW1uDQoNCmBgYHtyfQ0Kb3V0cHV0JHJlZmYgPC0gcGFyYW1ldGVyc1siYmV0YSJdL3BhcmFtZXRlcnNbImdhbW1hIl0gKiAgICAgICAgICAgICAgICAgICMgUjAgPSBiZXRhL2dhbW1hDQogICAgICAgICAgICAgICAgb3V0cHV0JFMvKG91dHB1dCRTK291dHB1dCRJK291dHB1dCRSKSAgICAgICAgICAgICAgICAgICAgIyBtdWx0aXBseSBSMCBieSB0aGUgcHJvcG9ydGlvbiBzdXNjZXB0aWJsZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgYXQgZWFjaCB0aW1lc3RlcC9mb3IgZWFjaCByb3cNCiMgSW4gdGhpcyBjYWxjdWxhdGlvbiwgdGhlIHRvdGFsIHBvcHVsYXRpb24gc2l6ZSAob3V0cHV0JFMrb3V0cHV0JEkrb3V0cHV0JFIpIGlzIGNhbGN1bGF0ZWQgZm9yIGVhY2ggdGltZXN0ZXANCiMgc28gdGhpcyBhcHByb2FjaCB3b3VsZCBhbHNvIGJlIGFwcHJvcHJpYXRlIGlmIHRoZSBwb3B1bGF0aW9uIHNpemUgdmFyaWVzIG92ZXIgdGltZQ0KDQpgYGANCg0KDQojIyBQbG90IFJlZmYNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG91dHB1dCwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBzcGVjaWZ5IG9iamVjdCBjb250YWluaW5nIGRhdGEgdG8gcGxvdA0KICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IHJlZmYpKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgYXNzaWduIGNvbHVtbnMgdG8gYXhlcyBhbmQgZ3JvdXBzDQogIGdlb21fbGluZSgpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyByZXByZXNlbnQgZGF0YSBhcyBsaW5lcw0KICB4bGFiKCJUaW1lIChkYXlzKSIpKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgYWRkIGxhYmVsIGZvciB4IGF4aXMNCiAgeWxhYigiUmVmZiIpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGFkZCBsYWJlbCBmb3IgeSBheGlzDQogIGxhYnModGl0bGUgPSAiRWZmZWN0aXZlIHJlcHJvZHVjdGlvbiBudW1iZXIgb3ZlciB0aW1lIikgICAgICAgICAgICAgICAgIyBhZGQgcGxvdCB0aXRsZQ0KYGBgDQoNCg==