Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop #343

Merged
merged 60 commits into from
Jan 31, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
feaa6d4
add more flexibility with delays (#305)
sbfnk Nov 18, 2022
4568af6
documentation fixes and deprecation warning (#335)
sbfnk Nov 23, 2022
9d13484
fixes to small issues introduced by PR #305 (#336)
sbfnk Nov 24, 2022
2c5a4ef
upper bound `frac_obs` by 1 (#340)
sbfnk Dec 9, 2022
20b302e
Merge branch 'main' into develop
seabbs Jan 18, 2023
0945d9b
Documentation: Fix report_plots example
seabbs Jan 18, 2023
0dacc19
merge changes from main
seabbs Jan 18, 2023
568e10c
Merge branch 'main' into develop
seabbs Jan 18, 2023
f911647
Catch change in max delay definition in report_cases()
seabbs Jan 18, 2023
44f0ba2
Merge branch 'main' into develop
seabbs Jan 19, 2023
3ff53b3
Hotfix: Fix estimate_infections() example for truncation which did no…
seabbs Jan 19, 2023
ba11198
Merge changes from main
seabbs Jan 20, 2023
fc2854b
update news file to use categories
seabbs Jan 23, 2023
51e441c
update news
seabbs Jan 23, 2023
d5149e2
Tests: Add tests for stan convolution functions
seabbs Jan 23, 2023
f6ddb66
Feature: Add fixed argument to get_dist functions
seabbs Jan 23, 2023
7e31395
Documentation: Make new left truncation clearer
seabbs Jan 23, 2023
d384baf
Package: Surface reversing of PMF to happen once and explicitly vs wi…
seabbs Jan 23, 2023
7f4cadc
Package: Check code compilation and reset testing for CI
seabbs Jan 23, 2023
1b3b0b3
Documnetation: Make sure fixed is documented in all get_dist children
seabbs Jan 23, 2023
3f96cea
Tests: Add generation_time_opts test that should pas
seabbs Jan 23, 2023
f8c4a58
move synthetic validation to its own folder
seabbs Jan 23, 2023
79758e2
Tests: Add a synthetic recovery test for estimate_secondary (prevalen…
seabbs Jan 23, 2023
1173581
Package: Review estimate_secondary code
seabbs Jan 23, 2023
4faefa1
Package: Clean up not currently generated syntethic validation figures
seabbs Jan 23, 2023
749eaa1
Package: Add support for passing generation time distribution to gene…
seabbs Jan 23, 2023
e1ccc55
Package: Use rev_ to indicate reversed PMFs and catch reverse induced…
seabbs Jan 23, 2023
0b0d21b
Package: Add random walk + stationary GP to synthetic recovery
seabbs Jan 23, 2023
c489845
Package: Fix synthetic validation
seabbs Jan 24, 2023
d0cf718
Package: First pass at vector of delay mean lower bounds - depends on…
seabbs Jan 24, 2023
d7fc4a2
Package: Use reals vs ints for lower bounds of course
seabbs Jan 24, 2023
8c1b6ba
Package: Arrggh vectorised lower bounds are not in stan 2.21
seabbs Jan 24, 2023
5d6ff41
Documentation: Refresh docs style
seabbs Jan 24, 2023
ebb8ba4
Package: Update namespace
seabbs Jan 24, 2023
0fd749b
Package: Restyle code and docs
seabbs Jan 24, 2023
f10d18b
Package: Add use of delays_lp for generation time and define truncate…
seabbs Jan 24, 2023
44ce892
Model: Use delays_lp for truncation and generation time to properly s…
seabbs Jan 24, 2023
4140bdf
Model: Catch missinng changes for truncation in estimate_secondary
seabbs Jan 24, 2023
1b9d952
Bug: Fix edge case when using mixed distributions so that seeding tim…
seabbs Jan 24, 2023
c6627e8
Docmentation: Refresh README
seabbs Jan 24, 2023
872b9ea
Bug: Fix edge case where generation_time_opts was ignoring max from g…
seabbs Jan 24, 2023
c63d92f
Model: Drop numeric stabilisation in convolve
seabbs Jan 24, 2023
743deaf
Bug: Update if/else to check for max in generation_time_opts when loo…
seabbs Jan 24, 2023
c6908e5
Documentation: Refresh docs
seabbs Jan 24, 2023
49a5994
Tests: Fix tests failing due to PR changes
seabbs Jan 24, 2023
f5fe5a6
Documentation: Move frac_obs change into breaking changes section of …
seabbs Jan 24, 2023
3bd5f61
Documentation: Check everything is included in pkgdown:
seabbs Jan 24, 2023
d3b4593
update synthetic validation
sbfnk Jan 25, 2023
9305115
Documentation: URL check and spelling
seabbs Jan 25, 2023
f1299a7
Merge branch 'develop' of https://github.com/epiforecasts/EpiNow2 int…
seabbs Jan 25, 2023
f965d85
Update R/create.R
seabbs Jan 27, 2023
b0ec85f
Update README.Rmd
seabbs Jan 27, 2023
25e6608
Update R/create.R
seabbs Jan 27, 2023
18338a0
Update R/estimate_truncation.R
seabbs Jan 27, 2023
62f984a
Update R/create.R
seabbs Jan 27, 2023
1fcfbc5
Update NEWS.md
seabbs Jan 27, 2023
f3b5887
clarify connection between forecast_ and simulate_ secondary
seabbs Jan 30, 2023
dae890a
update news to remove reference to improved run times
seabbs Jan 30, 2023
e30d803
refresh documentation
seabbs Jan 30, 2023
c54b74e
update CRAN comments after window devel check
seabbs Jan 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export(create_rt_data)
export(create_shifted_cases)
export(create_stan_args)
export(create_stan_data)
export(delay_dist)
export(delay_opts)
export(dist_fit)
export(dist_skel)
Expand All @@ -44,6 +45,7 @@ export(extract_inits)
export(extract_stan_param)
export(forecast_secondary)
export(gamma_dist_def)
export(generation_time_opts)
export(get_dist)
export(get_generation_time)
export(get_incubation_period)
Expand Down
27 changes: 22 additions & 5 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,43 @@
# EpiNow2 1.3.4

This is mainly a maintenance release focussing on bug fixes and package infrastructure updates along with a few quality of life improvements.
This release focusses on bug fixes and package infrastructure updates along with a few quality of life improvements such as enabling the use of fixed delays and generation times.

Thanks to @seabbs, and @sbfnk and for [SACEMA](https://sacema.org) for hosting @seabbs whilst some of the development work on this release was being done.

## Breaking changes

* To enable enhance functionality `trunc_opts()` now takes a single argument (`dist`) which defines the truncation delay rather than a arbitary list of arguments (which was previously used to define the distribution.

## Features

* Adds a new function `simulate_secondary()` for simulating secondary observations under the generative process model assumed by `estimate_secondary`.
seabbs marked this conversation as resolved.
Show resolved Hide resolved
* Adds support for fixed delays (mean only or fixed lognormal distributed) or truncations (fixed lognormal distributed), and for pre-computing these delays as well as generation times if they are fixed. By @sbfnk and @seabbs.
* The range of the `frac_obs` parameter has restricted with an upper bound of 1 to reflect its name and description. By @sbfnk in #340.

## Package

* Update the GitHub Action files to new versions.
* Switched to using `seq_along()` rather than `1:length()`.
* Switched to using `seq_along()` rather than `1:length()` in all package code.
* Fixed a broken example in the documentation for `regional_runtimes()`.
* Add compatibility changes for the latest version of `rstan` and `rstantools`.
* Remove legacy use of `pkgnet` for package dependency visualisation.
* Slight edits to the model outline for `estimate_infections()`.
* Restyled all code using `styler`.
* Dropped dependency on `RcppParallel`.
* Updated `report_cases` to work with the new `delay_opts` helper function.
* Added test coverage for `report_cases` though note this function will likely be deprecated in future releases.
* Adds a new function `simulate_secondary()` for simulating secondary observations under the generative process model assumed by `estimate_secondary`.
* Switched to `linewidth` in `plot_CrIs` rather than `size` to avoid issues with `ggplot2` 3.4.0.
* Set up validation against synthetic data to run as a CI check.
* Added tests for internal stan convolution functions.

## Documentation

* Slight edits to the model outline for `estimate_infections()`.
* Updated examples to make use of fixed distributions to improve run-times where appropriate.
seabbs marked this conversation as resolved.
Show resolved Hide resolved

## Bugs

* Fixed a bug in `simulate_infections()` where passing a custom number of samples would cause the input vector of R values to be replicated in a column-wise fashion meaning that the intended R trajectory was not simulated.
* Fixed a bug in the `estimate_infections()` deconvolution model where the generation time was not correctly being reversed.
* Added a new CI check to perform validation against simulated data.

# EpiNow2 1.3.3

Expand Down
99 changes: 46 additions & 53 deletions R/create.R
Original file line number Diff line number Diff line change
Expand Up @@ -374,13 +374,16 @@ create_obs_model <- function(obs = obs_opts(), dates) {
#' Create Stan Data Required for estimate_infections
#'
#' @description`r lifecycle::badge("stable")`
#' Takes the output of `stan_opts()` and converts it into a list understood by `stan`. Internally
#' calls the other `create_` family of functions to construct a single list for input into stan
#' with all data required present.
#' Takes the output of `stan_opts()` and converts it into a list understood by
#' `stan`. Internally calls the other `create_` family of functions to
#' construct a single list for input into stan with all data required present.
#'
#' @param shifted_cases A dataframe of delay shifted cases
#' @param truncation `r lifecycle::badge("experimental")` A list of options as generated by `trunc_opts()`
#' defining the truncation of observed data. Defaults to `trunc_opts()`. See `estimate_truncation()`
#' for an approach to estimating truncation from data.
#'
#' @param truncation `r lifecycle::badge("experimental")` A list of options as
#' generated by `trunc_opts()` defining the truncation of observed data.
#' Defaults to `trunc_opts()`. See `estimate_truncation()` for an approach to
#' estimating truncation from data.
#' @inheritParams create_gp_data
#' @inheritParams create_obs_model
#' @inheritParams create_rt_data
Expand All @@ -394,37 +397,14 @@ create_stan_data <- function(reported_cases, generation_time,
rt, gp, obs, delays, horizon,
backcalc, shifted_cases,
truncation) {
## make sure we have at least max_gt seeding time

## make sure we have at least gt_max seeding time
delays$seeding_time <- max(delays$seeding_time, generation_time$max)

## complete generation time parameters if not all are given
if (is.null(generation_time)) {
generation_time <- list(mean = 1)
}
for (param in c("mean_sd", "sd", "sd_sd")) {
if (!(param %in% names(generation_time))) generation_time[[param]] <- 0
}
## check if generation time is fixed
if (generation_time$sd == 0 && generation_time$sd_sd == 0) {
if ("max_gt" %in% names(generation_time)) {
if (generation_time$max_gt != generation_time$mean) {
stop(
"Error in generation time defintion: if max_gt(",
generation_time$max_gt,
") is given it must be equal to the mean (",
generation_time$mean,
")"
)
}
} else {
generation_time$max_gt <- generation_time$mean
}
if (any(generation_time$mean_sd > 0, generation_time$sd_sd > 0)) {
stop(
"Error in generation time definition: if sd_mean is 0 and ",
"sd_sd is 0 then mean_sd must be 0, too."
)
}
## for backwards compatibility call generation_time_opts internally
if (is.list(generation_time) &&
all(c("mean", "mean_sd", "sd", "sd_sd") %in% names(generation_time))) {
generation_time <- do.call(generation_time_opts, generation_time)
}

cases <- reported_cases[(delays$seeding_time + 1):(.N - horizon)]$confirm
Expand All @@ -434,13 +414,10 @@ create_stan_data <- function(reported_cases, generation_time,
shifted_cases = shifted_cases,
t = length(reported_cases$date),
horizon = horizon,
gt_mean_mean = generation_time$mean,
gt_mean_sd = generation_time$mean_sd,
gt_sd_mean = generation_time$sd,
gt_sd_sd = generation_time$sd_sd,
max_gt = generation_time$max,
burn_in = 0
)
# add gt data
data <- c(data, generation_time)
# add delay data
data <- c(data, delays)
# add truncation data
Expand All @@ -462,6 +439,10 @@ create_stan_data <- function(reported_cases, generation_time,
data$prior_infections <- ifelse(is.na(data$prior_infections) | is.null(data$prior_infections),
0, data$prior_infections
)
if (is.null(data$gt_weight)) {
## default: weigh by number of data points
data$gt_weight <- data$t - data$seeding_time - data$horizon
}
if (data$seeding_time > 1) {
safe_lm <- purrr::safely(stats::lm)
data$prior_growth <- safe_lm(log(confirm) ~ t, data = first_week)[[1]]
Expand Down Expand Up @@ -507,26 +488,35 @@ create_stan_data <- function(reported_cases, generation_time,
create_initial_conditions <- function(data) {
init_fun <- function() {
out <- list()
if (data$delays > 0) {
if (data$n_uncertain_mean_delays > 0) {
out$delay_mean <- array(purrr::map2_dbl(
data$delay_mean_mean, data$delay_mean_sd * 0.1,
data$delay_mean_mean[data$uncertain_mean_delays],
data$delay_mean_sd[data$uncertain_mean_delays] * 0.1,
~ rnorm(1, mean = .x, sd = .y)
))
}
if (data$n_uncertain_sd_delays > 0) {
out$delay_sd <- array(purrr::map2_dbl(
data$delay_sd_mean, data$delay_sd_sd * 0.1,
data$delay_sd_mean[data$uncertain_sd_delays],
data$delay_sd_sd[data$uncertain_sd_delays] * 0.1,
~ rnorm(1, mean = .x, sd = .y)
))
}
if (data$truncation > 0) {
out$truncation_mean <- array(rnorm(1,
mean = data$trunc_mean_mean,
sd = data$trunc_mean_sd * 0.1
))
out$truncation_sd <- array(truncnorm::rtruncnorm(1,
a = 0,
mean = data$trunc_sd_mean,
sd = data$trunc_sd_sd * 0.1
))
if (data$trunc_mean_sd > 0) {
out$truncation_mean <- array(rnorm(1,
mean = data$trunc_mean_mean,
sd = data$trunc_mean_sd * 0.1
))
}
if (data$trunc_sd_sd > 0) {
out$truncation_sd <- array(
truncnorm::rtruncnorm(1,
a = 0,
mean = data$trunc_sd_mean,
sd = data$trunc_sd_sd * 0.1
))
}
}
if (data$fixed == 0) {
out$eta <- array(rnorm(data$M, mean = 0, sd = 0.1))
Expand Down Expand Up @@ -578,11 +568,14 @@ create_initial_conditions <- function(data) {
}
if (data$obs_scale == 1) {
out$frac_obs <- array(truncnorm::rtruncnorm(1,
a = 0,
a = 0, b = 1,
mean = data$obs_scale_mean,
sd = data$obs_scale_sd * 0.1
))
}
if (data$week_effect > 0) {
out$day_of_week_simplex = array(rep(1 / data$week_effect, data$week_effect))
}
return(out)
}
return(init_fun)
Expand Down
80 changes: 80 additions & 0 deletions R/dist.R
Original file line number Diff line number Diff line change
Expand Up @@ -711,3 +711,83 @@ tune_inv_gamma <- function(lower = 2, upper = 21) {
)
return(out)
}

##' Delay distribution.
##'
##' @description `r lifecycle::badge("stable")`
##' Defines the parameters of a delay distribution
##' @param mean Numeric. If the only non-zero summary parameter
##' then this is the fixed interval of the delay distribution. If the `sd` is
##' non-zero then this is the mean of the distribution given by \code{dist}.
##' If this is not given a vector of empty vectors is returned.
##' @param sd Numeric, defaults to 0. Sets the standard deviation of the delay
##' distribution.
##' @param mean_sd Numeric, defaults to 0. Sets the standard deviation of the
##' uncertainty around the mean of the delay distribution.
##' @param sd_sd Numeric, defaults to 0. Sets the standard deviation of the
##' uncertainty around the sd of the delay distribution.
##' @param dist Character, defaults to "lognormal". The (discretised) distribution
##' to be used. If sd == 0 then the delay is fixed and a delta function will be
##' used whatever the choice here.
##' @param max Numeric, maximum value of the delay distribution
##' @param fixed Logical, defaults to `FALSE`. Should delays be treated
##' as coming from fixed (vs uncertain) distributions. Making this simplification
##' drastically reduces compute requirements.
##' @return A list of delay distribution options to be used downstream
##' @author Sebastian Funk
##' @export
delay_dist <- function(mean, sd = 0, mean_sd = 0, sd_sd = 0,
dist = c("lognormal", "gamma"), max = NULL,
fixed = FALSE) {
dist <- match.arg(dist)

if (missing(mean)) {
ret <- list(
mean_mean = numeric(0),
mean_sd = numeric(0),
sd_mean = numeric(0),
sd_sd = numeric(0),
fixed = integer(0),
dist = integer(0)
)
if (is.null(max)) {
ret$max <- integer(0)
} else {
ret$max <- max
}
} else {
ret <- list(
mean_mean = mean,
mean_sd = mean_sd,
sd_mean = sd,
sd_sd = sd_sd
)
if (fixed) {
ret$mean_sd <- 0
ret$sd_sd <- 0
}
ret$fixed <- as.integer(ret$mean_sd == 0 && ret$mean_sd == 0)

## check if it's a fixed value
if (ret$sd_mean == 0 && ret$sd_sd == 0) {
if (ret$mean_mean %% 1 != 0) {
stop(
"When a delay distribution is set to a constant ",
"(sd == 0 and sd_sd == 0) then the mean parameter ",
"must be an integer."
)
}
ret$max <- ret$mean_mean
if (ret$mean_sd > 0) {
stop(
"When a delay distribution has sd == 0 and ",
"sd_sd == 0 then mean_sd must be 0, too."
)
}
} else {
ret$max <- max
}
ret$dist <- which(eval(formals()[["dist"]]) == dist) - 1
}
return(lapply(ret, array))
}
Loading