From b911f4e6679bbef31c5e6090407a83164d34c80e Mon Sep 17 00:00:00 2001 From: Zachary Susswein Date: Tue, 10 Sep 2024 19:40:53 +0000 Subject: [PATCH 01/10] Fit EpiNow2 model with specified params --- DESCRIPTION | 2 + NAMESPACE | 4 + R/fit_model.R | 174 +++++++++++++++++++++++++++++ man/apply_exclusions.Rd | 12 +- man/fit_model.Rd | 33 ++++++ man/opts_formatter.Rd | 28 +++++ man/read_exclusions.Rd | 7 +- tests/testthat/test-fit_model.R | 189 ++++++++++++++++++++++++++++++++ 8 files changed, 441 insertions(+), 8 deletions(-) create mode 100644 R/fit_model.R create mode 100644 man/fit_model.Rd create mode 100644 man/opts_formatter.Rd create mode 100644 tests/testthat/test-fit_model.R diff --git a/DESCRIPTION b/DESCRIPTION index 8abee0b..262970e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -16,6 +16,7 @@ Description: Add logging, metadata handling, and data handling fitting hundreds of models in parallel. License: Apache License (>= 2) Encoding: UTF-8 +Remotes: github::epiforecasts/EpiNow2@bcf297cf36a93cc56123bc3c9e8cebfb1421a962 Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.2 Suggests: @@ -28,6 +29,7 @@ Imports: cli, DBI, duckdb, + EpiNow2 (>= 1.4.0), rlang URL: https://cdcgov.github.io/cfa-epinow2-pipeline/ Depends: diff --git a/NAMESPACE b/NAMESPACE index 31b0709..7de0aa9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -5,6 +5,10 @@ export(apply_exclusions) export(download_from_azure_blob) export(fetch_blob_container) export(fetch_credential_from_env_var) +export(fit_model) +export(format_delay_interval) +export(format_generation_interval) +export(format_right_truncation) export(read_data) export(read_disease_parameters) export(read_exclusions) diff --git a/R/fit_model.R b/R/fit_model.R new file mode 100644 index 0000000..8740792 --- /dev/null +++ b/R/fit_model.R @@ -0,0 +1,174 @@ +#' Fit an EpiNow2 model +#' +#' @param data As returned from [CFAEpiNow2Pipeline::read_data()] +#' @param parameters As returned from +#' [CFAEpiNow2Pipeline::read_disease_parameters()] +#' @param seed The random seed, used for both initialization by EpiNow2 in R and +#' sampling in Stan +#' @param horizon The number of days, as an integer, to forecast +#' @param priors A list of lists. The first level should contain the key `rt` +#' with elements `mean` and `sd` and the key `gp` with element `alpha_sd`. +#' @param sampler_opts A list. The Stan sampler options to be passed through +#' EpiNow2. It has required keys: `cores`, `chains`, `iter_warmup`, +#' `iter_sampling`, `max_treedepth`, and `adapt_delta`. +#' +#' @return A fitted model object of class `epinow` or, if model fitting fails, +#' an NA is returned with a warning +#' @export +fit_model <- function( + data, + parameters, + seed, + horizon, + priors, + sampler_opts) { + # Priors ------------------------------------------------------------------ + rt <- EpiNow2::rt_opts( + list( + mean = priors[["rt"]][["mean"]], + sd = priors[["rt"]][["sd"]] + ) + ) + gp <- EpiNow2::gp_opts( + alpha_sd = priors[["gp"]][["alpha_sd"]] + ) + + # Distributions ----------------------------------------------------------- + generation_time <- format_generation_interval( + parameters[["generation_interval"]] + ) + delays <- format_delay_interval( + parameters[["delay_interval"]] + ) + truncation <- format_right_truncation( + parameters[["right_truncation"]], + data + ) + + # Stan sampler ------------------------------------------------------------ + stan <- EpiNow2::stan_opts( + cores = sampler_opts[["cores"]], + chains = sampler_opts[["chains"]], + # NOTE: seed gets used twice -- as the seed passed here to the Stan sampler + # and below as the R PRNG seed for EpiNow2 initialization + seed = seed, + warmup = sampler_opts[["iter_warmup"]], + samples = sampler_opts[["iter_samples"]], + control = list( + adapt_delta = sampler_opts[["adapt_delta"]], + max_treedepth = sampler_opts[["max_treedepth"]] + ) + ) + + df <- data.frame( + confirm = data[["confirm"]], + date = as.Date(data[["reference_date"]]) + ) + rlang::try_fetch( + withr::with_seed(seed, { + EpiNow2::epinow( + df, + generation_time = generation_time, + delays = delays, + truncation = truncation, + horizon = horizon, + rt = rt, + gp = gp, + stan = stan, + verbose = interactive() + ) + }), + # Downgrade model erroring out to a warning so we can catch and return + error = function(cnd) { + cli::cli_warn( + "Model fitting failed. Returning NA.", + parent = cnd, + class = "failing_fit" + ) + NA + } + ) +} + +#' Format PMFs for EpiNow2 +#' +#' Format PMFs for use by EpiNow2. Delays or right truncation are optional and +#' can be skipped by passing an NA. +#' +#' @param pmf As returned from [CFAEpiNow2Pipeline::read_disease_parameters()]. +#' A PMF vector or an NA, if not applying the PMF to the model fit. +#' +#' @return An EpiNow2::*_opts() formatted object or NA with a message +#' @name opts_formatter +NULL + +#' @rdname opts_formatter +#' @export +format_generation_interval <- function(pmf) { + if ( + rlang::is_na(pmf) || rlang::is_null(pmf) + ) { + cli::cli_abort("No generation time PMF specified but is required", + class = "Missing_GI" + ) + } + + suppressWarnings({ + EpiNow2::generation_time_opts( + dist = EpiNow2::dist_spec( + pmf = pmf + ) + ) + }) +} + +#' @rdname opts_formatter +#' @export +format_delay_interval <- function(pmf) { + if (rlang::is_na(pmf) || rlang::is_null(pmf)) { + cli::cli_alert("Not adjusting for infection to case delay") + EpiNow2::delay_opts() + } else { + suppressWarnings({ + EpiNow2::delay_opts( + dist = EpiNow2::dist_spec( + pmf = pmf + ) + ) + }) + } +} + +#' @inheritParams fit_model +#' @rdname opts_formatter +#' @export +format_right_truncation <- function(pmf, data) { + if ( + rlang::is_na(pmf) || rlang::is_null(pmf) + ) { + cli::cli_alert("Not adjusting for right truncation") + EpiNow2::trunc_opts() + } else if (length(pmf) > nrow(data)) { + # Nasty bug we ran into where **left-hand** side of the PMF was being + # silently removed if length of the PMF was longer than the data, + # effectively eliminating the right-truncation correction + + cli::cli_abort( + c( + "Right truncation PMF longer than the data", + "PMF length: {.val {length(pmf)}}", + "Data length: {.val {nrow(data)}}", + "PMF can only be up to length as the data" + ), + class = "right_trunc_too_long" + ) + } else { + suppressWarnings({ + EpiNow2::trunc_opts( + dist = EpiNow2::dist_spec( + pmf = pmf + ) + ) + }) + } +} diff --git a/man/apply_exclusions.Rd b/man/apply_exclusions.Rd index a32529b..19dd37e 100644 --- a/man/apply_exclusions.Rd +++ b/man/apply_exclusions.Rd @@ -12,11 +12,15 @@ apply_exclusions(cases, exclusions) \item{exclusions}{A dataframe returned by \code{\link[=read_exclusions]{read_exclusions()}}} } \value{ -A dataframe with the same rows and schema as \code{cases} and the value in -the column \code{confirm} converted to NA in any rows that match a row in +A dataframe with the same rows and schema as \code{cases} where the value +in the column \code{confirm} converted to NA in any rows that match a row in \code{exclusions} } \description{ -NAs are skipped in model fitting by EpiNow2, so matched rows are excluded -from the model likelihood. +Mark selected points to be ignored in model fitting. This manual selection +occurs externally to the pipeline and is passed to the pipeline in an +exclusions file read with \code{\link[=read_exclusions]{read_exclusions()}}. Mechanically, the exclusions +are applied by converting specified points to NAs in the dataset. NAs are +skipped in model fitting by EpiNow2, so matched rows are excluded from the +model likelihood. } diff --git a/man/fit_model.Rd b/man/fit_model.Rd new file mode 100644 index 0000000..1d6a41f --- /dev/null +++ b/man/fit_model.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/fit_model.R +\name{fit_model} +\alias{fit_model} +\title{Fit an EpiNow2 model} +\usage{ +fit_model(data, parameters, seed, horizon, priors, sampler_opts) +} +\arguments{ +\item{data}{As returned from \code{\link[=read_data]{read_data()}}} + +\item{parameters}{As returned from +\code{\link[=read_disease_parameters]{read_disease_parameters()}}} + +\item{seed}{The random seed, used for both initialization by EpiNow2 in R and +sampling in Stan} + +\item{horizon}{The number of days, as an integer, to forecast} + +\item{priors}{A list of lists. The first level should contain the key \code{rt} +with elements \code{mean} and \code{sd} and the key \code{gp} with element \code{alpha_sd}.} + +\item{sampler_opts}{A list. The Stan sampler options to be passed through +EpiNow2. It has required keys: \code{cores}, \code{chains}, \code{iter_warmup}, +\code{iter_sampling}, \code{max_treedepth}, and \code{adapt_delta}.} +} +\value{ +A fitted model object of class \code{epinow} or, if model fitting fails, +an NA is returned with a warning +} +\description{ +Fit an EpiNow2 model +} diff --git a/man/opts_formatter.Rd b/man/opts_formatter.Rd new file mode 100644 index 0000000..4885dc1 --- /dev/null +++ b/man/opts_formatter.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/fit_model.R +\name{opts_formatter} +\alias{opts_formatter} +\alias{format_generation_interval} +\alias{format_delay_interval} +\alias{format_right_truncation} +\title{Format PMFs for EpiNow2} +\usage{ +format_generation_interval(pmf) + +format_delay_interval(pmf) + +format_right_truncation(pmf, data) +} +\arguments{ +\item{pmf}{As returned from \code{\link[=read_disease_parameters]{read_disease_parameters()}}. +A PMF vector or an NA, if not applying the PMF to the model fit.} + +\item{data}{As returned from \code{\link[=read_data]{read_data()}}} +} +\value{ +An EpiNow2::*_opts() formatted object or NA with a message +} +\description{ +Format PMFs for use by EpiNow2. Delays or right truncation are optional and +can be skipped by passing an NA. +} diff --git a/man/read_exclusions.Rd b/man/read_exclusions.Rd index 5c7ae0f..8e81673 100644 --- a/man/read_exclusions.Rd +++ b/man/read_exclusions.Rd @@ -7,8 +7,7 @@ read_exclusions(path) } \arguments{ -\item{path}{The path to the local file in \code{.csv} format with points to -exclude} +\item{path}{The path to the exclusions file in \code{.csv} format} } \value{ A dataframe with columns \code{reference_date}, \code{report_date}, @@ -24,6 +23,6 @@ Expects to read a CSV with required columns: } } \details{ -These columns have the same meaning as in \link{read_data}. Additional columns are -allowed and will be ignored by the reader. +These columns have the same meaning as in \code{\link[=read_data]{read_data()}}. Additional columns +are allowed and will be ignored by the reader. } diff --git a/tests/testthat/test-fit_model.R b/tests/testthat/test-fit_model.R new file mode 100644 index 0000000..d671dea --- /dev/null +++ b/tests/testthat/test-fit_model.R @@ -0,0 +1,189 @@ +test_that("Minimal model fit all params runs", { + # Parameters + parameters <- list( + generation_interval = gostic_gt_pmf, + delay_interval = c(0.2, 0.8), + right_truncation = c(0.7, 0.3) + ) + # Data -- 5 points only + data_path <- test_path("data", "test_data.parquet") + con <- DBI::dbConnect(duckdb::duckdb()) + data <- DBI::dbGetQuery(con, " + SELECT + report_date, + reference_date, + disease, + geo_value AS state_abb, + value AS confirm + FROM read_parquet(?) + ORDER BY reference_date + LIMIT 5 + ", + params = list(data_path) + ) + DBI::dbDisconnect(con) + # Priors + priors <- list( + rt = list( + mean = 1, + sd = 0.2 + ), + gp = list( + alpha_sd = 0.05 + ) + ) + # Sampler + sampler_opts <- list( + cores = 1, + chains = 1, + adapt_delta = 0.8, + max_treedepth = 10, + iter_warmup = 100, + iter_samples = 100 + ) + + fit <- fit_model( + data = data, + parameters = parameters, + seed = 12345, + horizon = 0, + priors = priors, + sampler = sampler_opts + ) + + expect_s3_class(fit, "epinow") +}) + +test_that("Minimal model fit with no right trunc or delay runs", { + # Parameters + parameters <- list( + generation_interval = gostic_gt_pmf, + delay_interval = NA, + right_truncation = NA + ) + # Data -- 5 points only + data_path <- test_path("data", "test_data.parquet") + con <- DBI::dbConnect(duckdb::duckdb()) + data <- DBI::dbGetQuery(con, " + SELECT + report_date, + reference_date, + disease, + geo_value AS state_abb, + value AS confirm + FROM read_parquet(?) + ORDER BY reference_date + LIMIT 5 + ", + params = list(data_path) + ) + DBI::dbDisconnect(con) + # Priors + priors <- list( + rt = list( + mean = 1, + sd = 0.2 + ), + gp = list( + alpha_sd = 0.05 + ) + ) + # Sampler + sampler_opts <- list( + cores = 1, + chains = 1, + adapt_delta = 0.8, + max_treedepth = 10, + iter_warmup = 100, + iter_samples = 100 + ) + + fit <- fit_model( + data = data, + parameters = parameters, + seed = 12345, + horizon = 0, + priors = priors, + sampler = sampler_opts + ) + + expect_s3_class(fit, "epinow") +}) + +test_that("Bad params w/ failing fit issues warning and returns NA", { + # Parameters + parameters <- list( + generation_interval = gostic_gt_pmf, + delay_interval = NA, + right_truncation = NA + ) + # Data -- 5 points only + data_path <- test_path("data", "test_data.parquet") + con <- DBI::dbConnect(duckdb::duckdb()) + data <- DBI::dbGetQuery(con, " + SELECT + report_date, + reference_date, + disease, + geo_value AS state_abb, + value AS confirm + FROM read_parquet(?) + ORDER BY reference_date + LIMIT 5 + ", + params = list(data_path) + ) + DBI::dbDisconnect(con) + # Priors + priors <- list( + rt = list( + mean = 1, + sd = 0.2 + ), + gp = list( + alpha_sd = 0.05 + ) + ) + # Sampler + sampler_opts <- list( + cores = 1, + chains = 1, + adapt_delta = 0.8, + max_treedepth = 10, + iter_warmup = -100, + iter_samples = 100 + ) + + expect_warning( + fit <- fit_model( + data = data, + parameters = parameters, + seed = 12345, + horizon = 0, + priors = priors, + sampler = sampler_opts + ), + class = "failing_fit" + ) + + expect_true(is.na(fit)) +}) + +test_that("Right truncation longer than data throws error", { + data <- data.frame(x = c(1, 2)) + right_truncation_pmf <- c(0.1, 0.2, 0.7) + + expect_error( + format_right_truncation( + right_truncation_pmf, + data + ), + class = "right_trunc_too_long" + ) +}) + +test_that("Missing GI throws error", { + expect_error(format_generation_interval(NA), + class = "Missing_GI" + ) +}) From fdc94ef86d90d2365b54c8480ab5cf093c85c767 Mon Sep 17 00:00:00 2001 From: Zachary Susswein Date: Tue, 10 Sep 2024 20:48:41 +0000 Subject: [PATCH 02/10] Bump NEWS --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index ca32799..a03ebca 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # CFAEpiNow2Pipeline (development version) +* Fit EpiNow2 model using params and fixed seed * Read and apply exclusions to case data * Data reader and processor * Parameters read from local parquet file or files From 5f1518906e18ef6a7c7bd2b7e3042ff10d3a4c5c Mon Sep 17 00:00:00 2001 From: Zachary Susswein <46581799+zsusswein@users.noreply.github.com> Date: Tue, 10 Sep 2024 19:29:33 -0400 Subject: [PATCH 03/10] Apply suggestions from code review Co-authored-by: Katie Gostic (she/her) --- R/fit_model.R | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/R/fit_model.R b/R/fit_model.R index 8740792..f026cd8 100644 --- a/R/fit_model.R +++ b/R/fit_model.R @@ -1,6 +1,6 @@ #' Fit an EpiNow2 model #' -#' @param data As returned from [CFAEpiNow2Pipeline::read_data()] +#' @param data, in the format returned by [CFAEpiNow2Pipeline::read_data()] #' @param parameters As returned from #' [CFAEpiNow2Pipeline::read_disease_parameters()] #' @param seed The random seed, used for both initialization by EpiNow2 in R and @@ -92,8 +92,13 @@ fit_model <- function( #' Format PMFs for EpiNow2 #' -#' Format PMFs for use by EpiNow2. Delays or right truncation are optional and -#' can be skipped by passing an NA. +#' Opinionated wrappers around EpiNow2::generation_time_opts(), +#' EpiNow2::delay_opts(), or EpiNow2::dist_spec() that +#' formats the generation interval, delay, or right truncation parameters +#' as an object ready for input to EpiNow2. +#' +#' Delays or right truncation are optional and +#' can be skipped by passing `pmf = NA`. #' #' @param pmf As returned from [CFAEpiNow2Pipeline::read_disease_parameters()]. #' A PMF vector or an NA, if not applying the PMF to the model fit. From d72a0de905f8967109b8983ee2e34574022848ec Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 23:30:04 +0000 Subject: [PATCH 04/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- R/fit_model.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/fit_model.R b/R/fit_model.R index f026cd8..553cb93 100644 --- a/R/fit_model.R +++ b/R/fit_model.R @@ -92,8 +92,8 @@ fit_model <- function( #' Format PMFs for EpiNow2 #' -#' Opinionated wrappers around EpiNow2::generation_time_opts(), -#' EpiNow2::delay_opts(), or EpiNow2::dist_spec() that +#' Opinionated wrappers around EpiNow2::generation_time_opts(), +#' EpiNow2::delay_opts(), or EpiNow2::dist_spec() that #' formats the generation interval, delay, or right truncation parameters #' as an object ready for input to EpiNow2. #' From f497e934d99d68f0d2084bcf4eb9dd4b9700ef55 Mon Sep 17 00:00:00 2001 From: zsusswein Date: Tue, 10 Sep 2024 23:41:27 +0000 Subject: [PATCH 05/10] Document --- man/fit_model.Rd | 2 +- man/opts_formatter.Rd | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/man/fit_model.Rd b/man/fit_model.Rd index 1d6a41f..d05b4f2 100644 --- a/man/fit_model.Rd +++ b/man/fit_model.Rd @@ -7,7 +7,7 @@ fit_model(data, parameters, seed, horizon, priors, sampler_opts) } \arguments{ -\item{data}{As returned from \code{\link[=read_data]{read_data()}}} +\item{data, }{in the format returned by \code{\link[=read_data]{read_data()}}} \item{parameters}{As returned from \code{\link[=read_disease_parameters]{read_disease_parameters()}}} diff --git a/man/opts_formatter.Rd b/man/opts_formatter.Rd index 4885dc1..266feb5 100644 --- a/man/opts_formatter.Rd +++ b/man/opts_formatter.Rd @@ -17,12 +17,18 @@ format_right_truncation(pmf, data) \item{pmf}{As returned from \code{\link[=read_disease_parameters]{read_disease_parameters()}}. A PMF vector or an NA, if not applying the PMF to the model fit.} -\item{data}{As returned from \code{\link[=read_data]{read_data()}}} +\item{data}{in the format returned by \code{\link[=read_data]{read_data()}}} } \value{ An EpiNow2::*_opts() formatted object or NA with a message } \description{ -Format PMFs for use by EpiNow2. Delays or right truncation are optional and -can be skipped by passing an NA. +Opinionated wrappers around EpiNow2::generation_time_opts(), +EpiNow2::delay_opts(), or EpiNow2::dist_spec() that +formats the generation interval, delay, or right truncation parameters +as an object ready for input to EpiNow2. +} +\details{ +Delays or right truncation are optional and +can be skipped by passing \code{pmf = NA}. } From 9bd8fa8dafb13d2e72a7bab6e7efb0c940c00e9f Mon Sep 17 00:00:00 2001 From: Zachary Susswein <46581799+zsusswein@users.noreply.github.com> Date: Wed, 11 Sep 2024 07:42:23 -0400 Subject: [PATCH 06/10] Update R/fit_model.R Co-authored-by: Adam Howes --- R/fit_model.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/fit_model.R b/R/fit_model.R index 553cb93..369af42 100644 --- a/R/fit_model.R +++ b/R/fit_model.R @@ -100,7 +100,7 @@ fit_model <- function( #' Delays or right truncation are optional and #' can be skipped by passing `pmf = NA`. #' -#' @param pmf As returned from [CFAEpiNow2Pipeline::read_disease_parameters()]. +#' @param pmf As returned by [CFAEpiNow2Pipeline::read_disease_parameters()]. #' A PMF vector or an NA, if not applying the PMF to the model fit. #' #' @return An EpiNow2::*_opts() formatted object or NA with a message From 439be4e214924ab62c646676dcaefaa90857dd04 Mon Sep 17 00:00:00 2001 From: Zachary Susswein Date: Wed, 11 Sep 2024 12:36:46 -0400 Subject: [PATCH 07/10] Clarify test parameterization --- tests/testthat/test-fit_model.R | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/testthat/test-fit_model.R b/tests/testthat/test-fit_model.R index d671dea..fb7f976 100644 --- a/tests/testthat/test-fit_model.R +++ b/tests/testthat/test-fit_model.R @@ -111,6 +111,11 @@ test_that("Minimal model fit with no right trunc or delay runs", { }) test_that("Bad params w/ failing fit issues warning and returns NA", { + # Parameterization is same as above except Stan argument `iter_warmup` is + # negative, which is an illegal parameterizaion. As a result, EpiNow2 starts + # the Stan sampler but it terminates unexpectedly with an error, which is the + # desired testing condition. + # Parameters parameters <- list( generation_interval = gostic_gt_pmf, From 30df9558732404f5b0981b8575dbe7c60cafc2dd Mon Sep 17 00:00:00 2001 From: Zachary Susswein Date: Wed, 11 Sep 2024 12:57:13 -0400 Subject: [PATCH 08/10] Reflow roxygen docstring --- R/fit_model.R | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/R/fit_model.R b/R/fit_model.R index 369af42..19fe864 100644 --- a/R/fit_model.R +++ b/R/fit_model.R @@ -93,15 +93,15 @@ fit_model <- function( #' Format PMFs for EpiNow2 #' #' Opinionated wrappers around EpiNow2::generation_time_opts(), -#' EpiNow2::delay_opts(), or EpiNow2::dist_spec() that -#' formats the generation interval, delay, or right truncation parameters -#' as an object ready for input to EpiNow2. +#' EpiNow2::delay_opts(), or EpiNow2::dist_spec() that formats the generation +#' interval, delay, or right truncation parameters as an object ready for input +#' to EpiNow2. #' -#' Delays or right truncation are optional and -#' can be skipped by passing `pmf = NA`. +#' Delays or right truncation are optional and can be skipped by passing `pmf = +#' NA`. #' -#' @param pmf As returned by [CFAEpiNow2Pipeline::read_disease_parameters()]. -#' A PMF vector or an NA, if not applying the PMF to the model fit. +#' @param pmf As returned by [CFAEpiNow2Pipeline::read_disease_parameters()]. A +#' PMF vector or an NA, if not applying the PMF to the model fit. #' #' @return An EpiNow2::*_opts() formatted object or NA with a message #' @name opts_formatter From e849eb52a9c8726cfff35efef13be60df9fe1c2a Mon Sep 17 00:00:00 2001 From: Zachary Susswein Date: Wed, 11 Sep 2024 13:00:47 -0400 Subject: [PATCH 09/10] Match formatting of `format()` functions --- R/fit_model.R | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/R/fit_model.R b/R/fit_model.R index 19fe864..b9bfb7a 100644 --- a/R/fit_model.R +++ b/R/fit_model.R @@ -130,7 +130,9 @@ format_generation_interval <- function(pmf) { #' @rdname opts_formatter #' @export format_delay_interval <- function(pmf) { - if (rlang::is_na(pmf) || rlang::is_null(pmf)) { + if ( + rlang::is_na(pmf) || rlang::is_null(pmf) + ) { cli::cli_alert("Not adjusting for infection to case delay") EpiNow2::delay_opts() } else { From 9cbc0b635bfa6001230c0cc5b7384f215eb56999 Mon Sep 17 00:00:00 2001 From: zsusswein Date: Wed, 11 Sep 2024 17:06:18 +0000 Subject: [PATCH 10/10] Document --- man/opts_formatter.Rd | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/man/opts_formatter.Rd b/man/opts_formatter.Rd index 266feb5..3c6e4a2 100644 --- a/man/opts_formatter.Rd +++ b/man/opts_formatter.Rd @@ -14,8 +14,8 @@ format_delay_interval(pmf) format_right_truncation(pmf, data) } \arguments{ -\item{pmf}{As returned from \code{\link[=read_disease_parameters]{read_disease_parameters()}}. -A PMF vector or an NA, if not applying the PMF to the model fit.} +\item{pmf}{As returned by \code{\link[=read_disease_parameters]{read_disease_parameters()}}. A +PMF vector or an NA, if not applying the PMF to the model fit.} \item{data}{in the format returned by \code{\link[=read_data]{read_data()}}} } @@ -24,11 +24,10 @@ An EpiNow2::*_opts() formatted object or NA with a message } \description{ Opinionated wrappers around EpiNow2::generation_time_opts(), -EpiNow2::delay_opts(), or EpiNow2::dist_spec() that -formats the generation interval, delay, or right truncation parameters -as an object ready for input to EpiNow2. +EpiNow2::delay_opts(), or EpiNow2::dist_spec() that formats the generation +interval, delay, or right truncation parameters as an object ready for input +to EpiNow2. } \details{ -Delays or right truncation are optional and -can be skipped by passing \code{pmf = NA}. +Delays or right truncation are optional and can be skipped by passing \code{pmf = NA}. }