This vignette shows the functions behind the article on what reported cases and deaths can identify in a stochastic SIRD model (Ospina, Oliveira, Ferraz and Gondim). The heavier computations of the article are shown but not run.
Simulated cases and deaths
sird_ga_simulate_inc() simulates the SIRD jump process
exactly and turns its daily increments into reported cases (binomial
thinning with probability p plus measurement error with
variance tau2 times the latent count) and reported deaths
(probability q, variance scale tau2_d).
set.seed(1)
N <- 20000
si <- sird_ga_simulate_inc(N, S0 = N - 10, I0 = 10, R0 = 0, lambda = 0.6, gamma = 0.3,
alpha = 0.1, p = 0.5, tau2 = 0.16, q = 1, tau2_d = 0.1, Tmax = 100)
c(cases = sum(si$cases), deaths = sum(si$deaths))
#> cases deaths
#> 5826 2988Fitting to cases and deaths
sird_ga_fit_inc() maximises the Kalman-filter
log-likelihood of the Gaussian approximation, with the death reporting
probability held at q = 1 (the product of q
and the death rate is what the death counts identify).
fit <- sird_ga_fit_inc(si$cases, si$deaths, N, use = "both",
starts = list(list(lambda = 0.5, gamma = 0.25, alpha = 0.15, i0 = 5e-4,
p = 0.6, tau2 = 0.3, tau2_d = 0.3)),
nsub = 5L)
round(fit$theta[c("lambda", "gamma", "alpha", "p")], 3)
#> lambda gamma alpha p
#> 0.662 0.323 0.110 0.491
fit$loglik
#> [1] 1536.861The death rate is not identifiable from cases alone
With cases only, recovery and death enter the likelihood through
gamma + alpha. The profile log-likelihood of
alpha is flat when fitted to cases and has a maximum when
deaths are added:
fit_c <- sird_ga_fit_inc(si$cases, NULL, N, use = "cases",
starts = list(list(lambda = 0.5, gamma = 0.25, alpha = 0.15,
i0 = 5e-4, p = 0.6, tau2 = 0.3)),
free = c("lambda", "gamma", "alpha", "i0", "p", "tau2"))
grid <- c(0.01, 0.05, 0.1, 0.2, 0.3)
sird_ga_profile(fit_c, si$cases, NULL, "alpha", grid)
sird_ga_profile(fit, si$cases, si$deaths, "alpha", grid)Paraíba: weekly data and transmission changing every four weeks
The application aggregates the daily counts of 2020 into complete
epidemiological weeks (daily counts carry a strong weekly reporting
cycle) and lets the transmission rate change every four weeks with
sird_ga_fit_tv(). Rates are then per week.
data(paraiba_covid19)
data(paraiba_population)
d <- paraiba_covid19
wk <- d$epi_week
keep <- wk %in% names(which(table(wk) == 7))
cases <- as.numeric(tapply(d$new_cases[keep], wk[keep], sum))
deaths <- as.numeric(tapply(d$new_deaths[keep], wk[keep], sum))
blk <- ceiling(seq_along(cases) / 4)
c(weeks = length(cases), blocks = max(blk))
#> weeks blocks
#> 40 10
rem <- 7 / 7 # removal rate per week for a mean infectious period of 7 days
st <- list(lambda = rep(1.2 * rem, max(blk)), gamma = 0.98 * rem, alpha = 0.02 * rem,
i0 = 5e-6, p = 0.2, tau2 = 20, tau2_d = 2)
f <- sird_ga_fit_tv(cases, deaths, paraiba_population, blk, "both", st, nsub = 20L)
7 / (f$theta[["gamma"]] + f$theta[["alpha"]]) # mean infectious period, days
tapply(f$R_t, blk, mean) # reproduction number by blockThe same model can be written with the general engine
(vignette("compartmental-models")): ga_model()
with infection, recovery and death events, ga_incidence()
streams for infection and death, and
blocks = list(lambda = blk) in ga_fit().
