|
1 |
| -#' Deprecated; use [forecast_infections()] instead |
| 1 | +#' Simulate infections using the renewal equation |
2 | 2 | #'
|
3 |
| -#' Calling this function passes all arguments to [forecast_infections()] |
4 |
| -#' @description `r lifecycle::badge("deprecated")` |
5 |
| -#' @param ... Arguments to be passed to [forecast_infections()] |
6 |
| -#' @return the result of [forecast_infections()] |
| 3 | +#' Simulations are done from given initial infections and, potentially |
| 4 | +#' time-varying, reproduction numbers. Delays and parameters of the observation |
| 5 | +#' model can be specified using the same options as in [estimate_infections()]. |
| 6 | +#' |
| 7 | +#' In order to simulate, all parameters that are specified such as the mean and |
| 8 | +#' standard deviation of delays or observation scaling, must be fixed. |
| 9 | +#' Uncertain parameters are not allowed. |
| 10 | +#' |
| 11 | +#' A previous function called [simulate_infections()] that simulates from a |
| 12 | +#' given model fit has been renamed [forecast_infections()]. Using |
| 13 | +#' [simulate_infections()] with existing estimates is now deprecated. This |
| 14 | +#' option will be removed in version 2.1.0. |
| 15 | +#' @param R a data frame of reproduction numbers (column `R`) by date (column |
| 16 | +#' `date`). Column `R` must be numeric and `date` must be in date format. If |
| 17 | +#' not all days between the first and last day in the `date` are present, |
| 18 | +#' it will be assumed that R stays the same until the next given date. |
| 19 | +#' @param initial_infections numeric; the initial number of infections. |
| 20 | +#' @param day_of_week_effect either `NULL` (no day of the week effect) or a |
| 21 | +#' numerical vector of length specified in [obs_opts()] as `week_length` |
| 22 | +#' (default: 7) if `week_effect` is set to TRUE. Each element of the vector |
| 23 | +#' gives the weight given to reporting on this day (normalised to 1). |
| 24 | +#' The default is `NULL`. |
| 25 | +#' @param estimates deprecated; use [forecast_infections()] instead |
| 26 | +#' @param ... deprecated; only included for backward compatibility |
| 27 | +#' @inheritParams estimate_infections |
| 28 | +#' @inheritParams rt_opts |
| 29 | +#' @inheritParams stan_opts |
| 30 | +#' @importFrom lifecycle deprecate_warn |
| 31 | +#' @importFrom checkmate assert_data_frame assert_date assert_numeric |
| 32 | +#' assert_subset |
| 33 | +#' @importFrom data.table data.table merge.data.table nafill rbindlist |
| 34 | +#' @return A data.table of simulated infections (variable `infections`) and |
| 35 | +#' reported cases (variable `reported_cases`) by date. |
| 36 | +#' @author Sebastian Funk |
7 | 37 | #' @export
|
8 |
| -simulate_infections <- function(...) { |
| 38 | +#' @examples |
| 39 | +#' \donttest{ |
| 40 | +#' R <- data.frame( |
| 41 | +#' date = seq.Date(as.Date("2023-01-01"), length.out = 14, by = "day"), |
| 42 | +#' R = c(rep(1.2, 7), rep(0.8, 7)) |
| 43 | +#' ) |
| 44 | +#' sim <- simulate_infections( |
| 45 | +#' R = R, |
| 46 | +#' initial_infections = 100, |
| 47 | +#' generation_time = generation_time_opts( |
| 48 | +#' fix_dist(example_generation_time) |
| 49 | +#' ), |
| 50 | +#' delays = delay_opts(fix_dist(example_reporting_delay)), |
| 51 | +#' obs = obs_opts(family = "poisson") |
| 52 | +#' ) |
| 53 | +#' } |
| 54 | +simulate_infections <- function(estimates, R, initial_infections, |
| 55 | + day_of_week_effect = NULL, |
| 56 | + generation_time = generation_time_opts(), |
| 57 | + delays = delay_opts(), |
| 58 | + truncation = trunc_opts(), |
| 59 | + obs = obs_opts(), |
| 60 | + CrIs = c(0.2, 0.5, 0.9), |
| 61 | + backend = "rstan", |
| 62 | + pop = 0, ...) { |
| 63 | + ## deprecated usage |
| 64 | + if (!missing(estimates)) { |
9 | 65 | deprecate_warn(
|
10 | 66 | "2.0.0",
|
11 |
| - "simulate_infections()", |
| 67 | + "simulate_infections(estimates)", |
12 | 68 | "forecast_infections()",
|
13 |
| - "A new [simulate_infections()] function for simulating from given ", |
14 |
| - "parameters is planned for implementation in the future." |
| 69 | + details = paste0( |
| 70 | + "This `estimates` option will be removed from [simulate_infections()] ", |
| 71 | + "in version 2.1.0." |
| 72 | + ) |
| 73 | + ) |
| 74 | + return(forecast_infections(estimates = estimates, ...)) |
| 75 | + } |
| 76 | + |
| 77 | + ## check inputs |
| 78 | + assert_data_frame(R, any.missing = FALSE) |
| 79 | + assert_subset(colnames(R), c("date", "R")) |
| 80 | + assert_date(R$date) |
| 81 | + assert_numeric(R$R, lower = 0) |
| 82 | + assert_numeric(initial_infections, lower = 0) |
| 83 | + assert_numeric(day_of_week_effect, lower = 0, null.ok = TRUE) |
| 84 | + assert_numeric(pop, lower = 0) |
| 85 | + assert_class(delays, "delay_opts") |
| 86 | + assert_class(obs, "obs_opts") |
| 87 | + assert_class(generation_time, "generation_time_opts") |
| 88 | + |
| 89 | + ## create R for all dates modelled |
| 90 | + all_dates <- data.table(date = seq.Date(min(R$date), max(R$date), by = "day")) |
| 91 | + R <- merge.data.table(all_dates, R, by = "date", all.x = TRUE) |
| 92 | + R <- R[, R := nafill(R, type = "locf")] |
| 93 | + ## remove any initial NAs |
| 94 | + R <- R[!is.na(R)] |
| 95 | + |
| 96 | + seeding_time <- get_seeding_time(delays, generation_time) |
| 97 | + if (seeding_time > 1) { |
| 98 | + ## estimate initial growth from initial reproduction number if seeding time |
| 99 | + ## is greater than 1 |
| 100 | + initial_growth <- (R$R[1] - 1) / mean(generation_time) |
| 101 | + } else { |
| 102 | + initial_growth <- numeric(0) |
| 103 | + } |
| 104 | + |
| 105 | + data <- list( |
| 106 | + n = 1, |
| 107 | + t = nrow(R) + seeding_time, |
| 108 | + seeding_time = seeding_time, |
| 109 | + future_time = 0, |
| 110 | + initial_infections = array(log(initial_infections), dim = c(1, 1)), |
| 111 | + initial_growth = array(initial_growth, dim = c(1, length(initial_growth))), |
| 112 | + R = array(R$R, dim = c(1, nrow(R))), |
| 113 | + pop = pop |
| 114 | + ) |
| 115 | + |
| 116 | + data <- c(data, create_stan_delays( |
| 117 | + gt = generation_time, |
| 118 | + delay = delays, |
| 119 | + trunc = truncation |
| 120 | + )) |
| 121 | + |
| 122 | + if ((length(data$delay_mean_sd) > 0 && any(data$delay_mean_sd > 0)) || |
| 123 | + (length(data$delay_sd_sd) > 0 && any(data$delay_sd_sd > 0))) { |
| 124 | + stop( |
| 125 | + "Cannot simulate from uncertain parameters. Use the [fix_dist()] ", |
| 126 | + "function to set the parameters of uncertain distributions either the ", |
| 127 | + "mean or a randomly sampled value" |
15 | 128 | )
|
16 |
| - forecast_infections(...) |
| 129 | + } |
| 130 | + data$delay_mean <- array( |
| 131 | + data$delay_mean_mean, dim = c(1, length(data$delay_mean_mean)) |
| 132 | + ) |
| 133 | + data$delay_sd <- array( |
| 134 | + data$delay_sd_mean, dim = c(1, length(data$delay_sd_mean)) |
| 135 | + ) |
| 136 | + data$delay_mean_sd <- NULL |
| 137 | + data$delay_sd_sd <- NULL |
| 138 | + |
| 139 | + data <- c(data, create_obs_model( |
| 140 | + obs, dates = R$date |
| 141 | + )) |
| 142 | + |
| 143 | + if (data$obs_scale_sd > 0) { |
| 144 | + stop( |
| 145 | + "Cannot simulate from uncertain observation scaling; use fixed scaling ", |
| 146 | + "instead." |
| 147 | + ) |
| 148 | + } |
| 149 | + if (data$obs_scale) { |
| 150 | + data$frac_obs <- array(data$obs_scale_mean, dim = c(1, 1)) |
| 151 | + } else { |
| 152 | + data$frac_obs <- array(dim = c(1, 0)) |
| 153 | + } |
| 154 | + data$obs_scale_mean <- NULL |
| 155 | + data$obs_scale_sd <- NULL |
| 156 | + |
| 157 | + if (obs$family == "negbin") { |
| 158 | + if (data$phi_sd > 0) { |
| 159 | + stop( |
| 160 | + "Cannot simulate from uncertain overdispersion; use fixed ", |
| 161 | + "overdispersion instead." |
| 162 | + ) |
| 163 | + } |
| 164 | + data$rep_phi <- array(data$phi_mean, dim = c(1, 1)) |
| 165 | + } else { |
| 166 | + data$rep_phi <- array(dim = c(1, 0)) |
| 167 | + } |
| 168 | + data$phi_mean <- NULL |
| 169 | + data$phi_sd <- NULL |
| 170 | + |
| 171 | + ## day of week effect |
| 172 | + if (is.null(day_of_week_effect)) { |
| 173 | + day_of_week_effect <- rep(1, data$week_effect) |
| 174 | + } |
| 175 | + |
| 176 | + day_of_week_effect <- day_of_week_effect / sum(day_of_week_effect) |
| 177 | + data$day_of_week_simplex <- array( |
| 178 | + day_of_week_effect, dim = c(1, data$week_effect) |
| 179 | + ) |
| 180 | + |
| 181 | + # Create stan arguments |
| 182 | + stan <- stan_opts(backend = backend, chains = 1, samples = 1, warmup = 1) |
| 183 | + args <- create_stan_args( |
| 184 | + stan, data = data, fixed_param = TRUE, model = "simulate_infections", |
| 185 | + verbose = FALSE |
| 186 | + ) |
| 187 | + |
| 188 | + ## simulate |
| 189 | + sim <- fit_model(args, id = "simulate_infections") |
| 190 | + |
| 191 | + ## join batches |
| 192 | + dates <- c( |
| 193 | + seq(min(R$date) - seeding_time, min(R$date) - 1, by = "day"), |
| 194 | + R$date |
| 195 | + ) |
| 196 | + out <- extract_parameter_samples(sim, data, |
| 197 | + reported_inf_dates = dates, |
| 198 | + reported_dates = dates[-(1:seeding_time)], |
| 199 | + drop_length_1 = TRUE |
| 200 | + ) |
| 201 | + |
| 202 | + out <- rbindlist(out[c("infections", "reported_cases")], idcol = "variable") |
| 203 | + out <- out[, c("sample", "parameter", "time") := NULL] |
| 204 | + |
| 205 | + return(out[]) |
17 | 206 | }
|
18 | 207 |
|
19 | 208 | #' Forecast infections from a given fit and trajectory of the time-varying
|
|
0 commit comments