Epidemic modelling of COVID-19 in the UK using an SIR model

I used to do a bit of epidemic modelling during my computer science degree and decided to re-map some of the models using the latest UK data for Coronavirus found here.

(Wikipedia is obviously not the most reputable source of data in the world! however it also aligns to data from other sources e.g. worldometers and theguardian)

This blog was inspired from a similar model used to map the epidemic in Germany, found here

The model used is an SIR (Susceptible, Infected, Recovered) compartmental epidemic model based on the following three Ordinary Differential Equations (ODEs):

Screenshot 2020-03-20 at 10.48.50.png
Fig. 1

Where S is the number of Susceptible population, I is the number of Infected, R is the Recovered population, and N is the sum of these three. β is the contact rate (average number of contacts per person per time) and γ is the rate of recovery. We can also determine the reproductive number (R0 = β / γ), R0 represents the expected number of new infections from a single infection.

These differential equations govern the rate of change between the different compartments, and can be used to predict future development of an epidemic. Before we can predict the future of the epidemic, we first need to find the best fit of the parameters of the model (β and γ) given the number of confirmed cases observed so far. (See here for more information on the SIR model)

Firstly if we look at the current confirmed cases in the UK, we can plot a graph of how the epidemic has spread so far:

# UK
Infected <- c(2,2,3,3,4,8,8,9,13,13,19,23,35,40,51,85,114,160,206,271,321,373,456,590,797,1061,1391,1543,1950,2626,3269)
Day <- 1:(length(Infected))
N <- 66000000 # pupulation of the UK

old <- par(mfrow = c(1, 2))
plot(Day, Infected, type ="b")
plot(Day, Infected, log = "y")
abline(lm(log10(Infected) ~ Day))
title("Confirmed infections COVID-19 in the UK", outer = TRUE, line = -2)
ConfirmedCasesUK.png

As we can see from both graphs, and in particular the right hand side log scale graph, the current spread has been exponential. At the time of writing (19th March 2020) the confirmed number of cases in the UK stands at 3,269. Please also note that this is based on confirmed cases (The number of actual cases is unknown but will be higher).

The next step is to fit the above SIR model to the data we’ve got so far for the UK, to achieve this I’m using the R ‘optim’ function to optimise the parameter values given the current observed data. (‘optim’ is a general-purpose optimisation based on Nelder–Mead, quasi-Newton and conjugate-gradient algorithms, here we use the L-BFGS-B method of Byrd et. al. 1995. See here for more details). The ‘ode’ function of deSolve is then used to evaluate the ODEs (in Fig. 1) using the optimised parameters.

# UK
Infected <- c(2,2,3,3,4,8,8,9,13,13,19,23,35,40,51,85,114,160,206,271,321,373,456,590,797,1061,1391,1543,1950,2626,3269)
Day <- 1:(length(Infected))
N <- 66000000 # pupulation of the UK

old <- par(mfrow = c(1, 2))
plot(Day, Infected, type ="b")
plot(Day, Infected, log = "y")
abline(lm(log10(Infected) ~ Day))
title("Confirmed infections COVID-19 in the UK", outer = TRUE, line = -2)

SIR <- function(time, state, parameters) {
  par <- as.list(c(state, parameters))
  with(par, {
    dS <- -beta/N * I * S
    dI <- beta/N * I * S - gamma * I
    dR <- gamma * I
    list(c(dS, dI, dR))
    })
}

library(deSolve)
init <- c(S = N-Infected[1], I = Infected[1], R = 0)
RSS <- function(parameters) {
  names(parameters) <- c("beta", "gamma")
  out <- ode(y = init, times = Day, func = SIR, parms = parameters)
  fit <- out[ , 3]
  sum((Infected - fit)^2)
}
 
Opt <- optim(c(0.5, 0.5), RSS, method = "L-BFGS-B", lower = c(0, 0), upper = c(1, 1)) # optimize with some sensible conditions
Opt$message
## [1] "CONVERGENCE: REL_REDUCTION_OF_F <= FACTR*EPSMCH"
 
Opt_par <- setNames(Opt$par, c("beta", "gamma"))
print(Opt_par)
##      beta     gamma 
## 0.6746089 0.3253912
 
t <- 1:90 # time in days
fit <- data.frame(ode(y = init, times = t, func = SIR, parms = Opt_par))
col <- 1:3 # colour
print(fit) 
matplot(fit$time, fit[ , 2:4], type = "l", xlab = "Day", ylab = "Number of subjects", lwd = 2, lty = 1, col = col)
 matplot(fit$time, fit[ , 2:4], type = "l", xlab = "Day", ylab = "Number of subjects", lwd = 2, lty = 1, col = col, log = "y")

points(Day, Infected)
legend("bottomright", c("Susceptibles", "Infecteds", "Recovereds"), lty = 1, lwd = 2, col = col, inset = 0.05)
title("Predicted Cases 2019-nCoV UK (worst case)", outer = TRUE, line = -2)

This gives us the optimal parameter values: β = 0.6235803, γ = 0.3764198, R0 = 1.7.

Recall from above the key value R0 (basic reproduction number) is the expected number of new infections from a single infection. Therefore the model predicts that each person will infect nearly 2 others (on average).

The following graph showing the predicted confirmed number of cases in the short term (3-5 days)

And the following data table shows the predicted values for the upcomming short term range:

DatePredicted Confirmed Cases
Thu 19th March3320
Fri 20th March4250
Sat 21st March5441
Sunday 22nd March6966
Mon 23rd March8917

Of course, the above predictions are based on the epidemic progressing as it currently has been, i.e. it doesn’t account for additional measures put in place to curb the spread of the virus (e.g. School closures, social distancing, lockdowns etc…), and so hopefully the actual numbers will be lower than the above predictions. However if the spread continues as current then the model predicts there will be between 8-9 thousand confirmed cases by Monday (23rd March 2020).

We can extrapolate the date range further to understand the potential impact that the virus could have if it continues to spread as it has been in the longer term – up to 90 days

Please keep in mind that this is a worst case prediction, as you can see we are currently in the very early stages of the epidemic spread (assuming no additional action is taken to prevent the spread) and the total Infected confirmed cases would peak at just over 6 Million (~10% of the UK population) on the 68th day of the outbreak (That would be on Sunday 26th April 2020). (For the full data please see Appendix 1).

Please also note that similar predictions were estimated for China (up to 200 million being Infected – significantly larger due to the larger population), however the actual recorded number of Infected people was significantly less and peaked at a maximum so far of 80,000 where it seems to have become stable – potentially as a result of the thorough social distancing and lockdown applied in China. See here for a more detailed model of the epidemic in China and here for more data on China (where you can see that the epidemic is limiting out at 80k).

A last note would be to highlight these models are simplistic in nature compared to the extremely complex dynamics of an actual epidemic, in particular they don’t take into account measures taken to restrict the spread of the virus.

Further extensions to the model could also consider:

  • Using an SEIR model (adding an Exposed compartment for people who are infected but not yet infectious)
  • Adding a “Q” layer since a lot of people are being Quarantined or isolated
  • considering the “hidden” population that is infected but is denied being tested due to shortage of tests
  • Feasibility of a second wave / outbreak of the epidemic later in the year (as seen in previous outbreaks, such as Swine Flu)

Update 27/03/2020

Below is an updated model for the short term (using the latest data over the last week):

DatePredicted Confirmed Cases
Friday 27th March15,417
Saturday 28th March19,492
Sunday 29nd March24,636
Monday 30rd March31,127

Using the latest data over the last week has also shown a reduction in the predicted peak cases which would now peak at just over 2million on the 66th day (That would be on Sunday 24th April 2020). (For the full data please see Appendix 2).

Update 29/03/20

As of Sunday 29th March 2020, the following graph shows a re-plot of the confirmed Infected cases:

As we can see from the log scale graph on the right hand side, the number of cases seems to be falling away from the linear line, showing that the exponential curve may be dropping away slightly – possibly as a result of measures taken to prevent the spread of the virus (such as isolation). Hopefully this will continue to drop away once impacts of isolation become more clear over the coming few weeks (there may be a delayed impact due to the recent changes in UK government advice and the incubation period of the infection).

GitHub Link for the code: https://github.com/tomwilding/covid-19Model-R/blob/master/sir.r

Full Data for models at different dates:

Appendix 1. 20/03/20 full data for the 90 days predicted by the model

DateInfectedSusceptibleRecovered
Tuesday, 18 February 20202659999980.00
Wednesday, 19 February 20203659999970.85
Thursday, 20 February 20203659999951.95
Friday, 21 February 20204659999923.35
Saturday, 22 February 20205659999895.14
Sunday, 23 February 20207659999867.44
Monday, 24 February 202096599998110.37
Tuesday, 25 February 2020116599997514.14
Wednesday, 26 February 2020146599996718.96
Thursday, 27 February 2020186599995625.12
Friday, 28 February 2020246599994333.02
Saturday, 29 February 2020306599992743.14
Sunday, 1 March 2020396599990556.08
Monday, 2 March 2020506599987872.66
Tuesday, 3 March 2020646599984293.89
Wednesday, 4 March 20208165999797121.07
Thursday, 5 March 202010465999740155.87
Friday, 6 March 202013465999666200.43
Saturday, 7 March 202017165999571257.48
Sunday, 8 March 202021965999450330.53
Monday, 9 March 202028065999296424.05
Tuesday, 10 March 202035965999097543.80
Wednesday, 11 March 202046065998843697.13
Thursday, 12 March 202058965998518893.44
Friday, 13 March 2020754659981021144.79
Saturday, 14 March 2020965659975681466.61
Sunday, 15 March 20201235659968861878.66
Monday, 16 March 20201582659960122406.22
Tuesday, 17 March 20202025659948933081.67
Wednesday, 18 March 20202593659934613946.48
Thursday, 19 March 20203320659916275053.68
Friday, 20 March 20204250659892796471.22
Saturday, 21 March 20205441659862738286.04
Sunday, 22 March 202069666598242510609.39
Monday, 23 March 202089176597749913583.67
Tuesday, 24 March 2020114156597119417391.08
Wednesday, 25 March 2020146116596312422264.70
Thursday, 26 March 2020187006595279728502.60
Friday, 27 March 2020239316593958336485.92
Saturday, 28 March 2020306216592267746701.75
Sunday, 29 March 2020391756590105359772.29
Monday, 30 March 2020501066587340376491.82
Tuesday, 31 March 2020640686583805997873.39
Wednesday, 1 April 20208188965792903125207.80
Thursday, 2 April 202010461765735246160137.10
Friday, 3 April 202013357165661682204747.30
Saturday, 4 April 202017040465567915261681.40
Sunday, 5 April 202021717665448545334279.00
Monday, 6 April 202027643365296823426744.10
Tuesday, 7 April 202035129065104367544343.30
Wednesday, 8 April 202044550364860862693634.80
Thursday, 9 April 202056352264553758882719.80
Friday, 10 April 2020710486641680081121506.00
Saturday, 11 April 2020892126636859231421951.00
Sunday, 12 April 20201114516630872371798247.00
Monday, 13 April 20201383608623495262266866.00
Tuesday, 14 April 20201704460614491562846384.00
Wednesday, 15 April 20202080140603629153556945.00
Thursday, 16 April 20202510292590704564419252.00
Friday, 17 April 20202989535575574855452979.00
Saturday, 18 April 20203506005558194006674595.00
Sunday, 19 April 20204040542538647228094736.00
Monday, 20 April 20204567093517174129715495.00
Tuesday, 21 April 202050547424941710811528150.00
Wednesday, 22 April 202054713454701667913511980.00
Thursday, 23 April 202057881264457724115634630.00
Friday, 24 April 202059841154216158517854300.00
Saturday, 25 April 202060492163982754820123240.00
Sunday, 26 April 202059851083762276222392130.00
Monday, 27 April 202058039603558169424614350.00
Tuesday, 28 April 202055255473372509126749360.00
Wednesday, 29 April 202051737153206130228764980.00
Thursday, 30 April 202047730603058871730638220.00
Friday, 1 May 202043463732929854732355080.00
Saturday, 2 May 202039130482817742433909530.00
Sunday, 3 May 202034883742720954635302080.00
Monday, 4 May 202030835112637827436538210.00
Tuesday, 5 May 202027058842566721937626900.00
Wednesday, 6 May 202023598002506092238579280.00
Thursday, 7 May 202020471222454523139407650.00
Friday, 8 May 202017679112410745240124640.00
Saturday, 9 May 202015209712373637640742650.00
Sunday, 10 May 202013043042342221241273480.00
Monday, 11 May 202011154492315647641728070.00
Tuesday, 12 May 20209517372293187042116390.00
Wednesday, 13 May 20208104682274213742447400.00
Thursday, 14 May 20206890312258193642729030.00
Friday, 15 May 20205849742244672142968300.00
Saturday, 16 May 20204960492233262843171320.00
Sunday, 17 May 20204202252223638243343390.00

Appendix 2. 27/03/20 Below is the full data for the 90 days predicted by the model

DateInfectedSusceptibleRecovered
Tuesday, 18 February 2020266439998.000.00
Wednesday, 19 February 2020366439996.001.72
Thursday, 20 February 2020366439993.003.91
Friday, 21 February 2020466439989.006.67
Saturday, 22 February 2020566439985.0010.16
Sunday, 23 February 2020666439979.0014.59
Monday, 24 February 2020866439972.0020.19
Tuesday, 25 February 20201066439962.0027.28
Wednesday, 26 February 20201366439951.0036.25
Thursday, 27 February 20201766439936.0047.60
Friday, 28 February 20202166439917.0061.97
Saturday, 29 February 20202766439893.0080.17
Sunday, 1 March 20203466439863.00103.19
Monday, 2 March 20204366439825.00132.33
Tuesday, 3 March 20205466439777.00169.22
Wednesday, 4 March 20206966439716.00215.91
Thursday, 5 March 20208766439638.00275.00
Friday, 6 March 202011066439540.00349.80
Saturday, 7 March 202013966439417.00444.47
Sunday, 8 March 202017666439260.00564.29
Monday, 9 March 202022366439061.00715.95
Tuesday, 10 March 202028266438810.00907.91
Wednesday, 11 March 202035766438492.001150.87
Thursday, 12 March 202045266438090.001458.37
Friday, 13 March 202057266437581.001847.58
Saturday, 14 March 202072366436936.002340.19
Sunday, 15 March 202091666436121.002963.67
Monday, 16 March 2020115966435089.003752.76
Tuesday, 17 March 2020146666433782.004751.46
Wednesday, 18 March 2020185666432129.006015.41
Thursday, 19 March 2020234966430036.007615.01
Friday, 20 March 2020297266427388.009639.36
Saturday, 21 March 2020376166424037.0012201.11
Sunday, 22 March 2020476066419798.0015442.78
Monday, 23 March 2020602266414433.0019544.56
Tuesday, 24 March 2020761966407647.0024734.23
Wednesday, 25 March 2020963866399062.0031299.65
Thursday, 26 March 20201219166388204.0039604.45
Friday, 27 March 20201541766374475.0050107.71
Saturday, 28 March 20201949266357120.0063388.63
Sunday, 29 March 20202463666335186.0080177.36
Monday, 30 March 20203112766307479.00101393.30
Tuesday, 31 March 20203931066272497.00128192.70
Wednesday, 1 April 20204961466228359.00162027.00
Thursday, 2 April 20206257266172714.00204714.10
Friday, 3 April 20207884166102634.00258525.00
Saturday, 4 April 20209922166014492.00326286.30
Sunday, 5 April 202012468565903815.00411500.50
Monday, 6 April 202015639065765127.00518483.10
Tuesday, 7 April 202019569865591789.00652512.50
Wednesday, 8 April 202024417465375840.00819985.80
Thursday, 9 April 202030355365107883.001028564.00
Friday, 10 April 202037568164777036.001287283.00
Saturday, 11 April 202046238564371023.001606592.00
Sunday, 12 April 202056526463876469.001998267.00
Monday, 13 April 202068539363279481.002475127.00
Tuesday, 14 April 202082291262566603.003050485.00
Wednesday, 15 April 202097655461726190.003737256.00
Thursday, 16 April 2020114315660750156.004546688.00
Friday, 17 April 2020131729459635940.005486766.00
Saturday, 18 April 2020149119958388376.006560425.00
Sunday, 19 April 2020165513157021002.007763867.00
Monday, 20 April 2020179828555556349.009085366.00
Tuesday, 21 April 2020191017754024859.0010504960.00
Wednesday, 22 April 2020198224252462438.0011995320.00
Thursday, 23 April 2020200922850907032.0013523740.00
Friday, 24 April 2020199002249394912.0015055070.00
Saturday, 25 April 2020192766047957433.0016554910.00
Sunday, 26 April 2020182860246618841.0017992560.00
Monday, 27 April 2020170148145395341.0019343180.00
Tuesday, 28 April 2020155574644295340.0020588910.00
Wednesday, 29 April 2020140045643320484.0021719060.00
Thursday, 30 April 2020124345042467148.0022729400.00
Friday, 1 May 2020109090641727993.0023621100.00
Saturday, 2 May 202094724341093386.0024399370.00
Sunday, 3 May 202081526840552569.0025072160.00
Monday, 4 May 202069643540094519.0025649050.00
Tuesday, 5 May 202059116439708556.0026140280.00
Wednesday, 6 May 202049913839384712.0026556150.00
Thursday, 7 May 202041956139113942.0026906500.00
Friday, 8 May 202035135838888201.0027200440.00
Saturday, 9 May 202029333138700450.0027446220.00
Sunday, 10 May 202024425638544601.0027651140.00
Monday, 11 May 202020295738415443.0027821600.00
Tuesday, 12 May 202016834338308547.0027963110.00
Wednesday, 13 May 202013942738220173.0028080400.00
Thursday, 14 May 202011533938147176.0028177480.00
Friday, 15 May 20209531738086927.0028257760.00
Saturday, 16 May 20207870538037229.0028324070.00
Sunday, 17 May 20206494537996255.0028378800.00
This entry was posted in Uncategorized and tagged , , , , . Bookmark the permalink.

2 Responses to Epidemic modelling of COVID-19 in the UK using an SIR model

  1. Thanks for this article!

    I really found it interesting so I have included it in my article covering the top R resources on the virus (https://www.statsandr.com/blog/top-r-resources-on-covid-19-coronavirus/).

    Let me know if there is any inconsistency or if you prefer that I don’t mention it.

    • Thanks Antoine for linking to your blog – such a great idea to collaborate resources together in this way. I’ve updated the UK model with the latest data from this week.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s