Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 1 addition & 1 deletion .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
R_KEEP_PKG_SOURCE: yes
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: r-lib/actions/setup-r@v2
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/doc-preview.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
# Needed to read the PR branch
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
# Checkout the PR branch
ref: refs/pull/${{ github.event.issue.number }}/head
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pkgdown.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
DELPHI_EPIDATA_KEY: ${{ secrets.SECRET_EPIPREDICT_GHACTIONS_DELPHI_EPIDATA_KEY }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: r-lib/actions/setup-pandoc@v2

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-commands.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: r-lib/actions/pr-fetch@v2
with:
Expand Down
6 changes: 3 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: epipredict
Title: Basic epidemiology forecasting methods
Version: 0.2.6
Version: 0.2.7
Authors@R: c(
person("Daniel J.", "McDonald", , "daniel@stat.ubc.ca", role = c("aut", "cre")),
person("Ryan", "Tibshirani", , "ryantibs@cmu.edu", role = "aut"),
Expand All @@ -21,7 +21,7 @@ Description: A forecasting "framework" for creating epidemiological
are included.
License: MIT + file LICENSE
URL: https://github.com/cmu-delphi/epipredict/,
https://cmu-delphi.github.io/epipredict
https://cmu-delphi.github.io/epipredict/
BugReports: https://github.com/cmu-delphi/epipredict/issues/
Depends:
epidatasets,
Expand All @@ -31,7 +31,7 @@ Imports:
checkmate,
cli,
dplyr,
epiprocess (>= 0.11.2),
epiprocess (>= 0.13.0),
generics,
ggplot2,
glue,
Expand Down
12 changes: 11 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@

Pre-1.0.0 numbering scheme: 0.x will indicate releases, while 0.0.x will indicate PR's.

# epipredict 0.2.7

- `autoplot.canned_epipred()` had internal code using
`epiprocess:::autoplot.epi_df(.max_facets = Inf)`. This argument no longer
exists there, so it caused an error (noted by rebuilding the `README.Rmd`)


# epipredict 0.2.6

- `arx_forecaster()` and `flatline_forecaster()` now error early when `quantile_by_key` contains columns that are not keys of the input `epi_df`, rather than silently dropping the invalid keys (#229).
- `arx_forecaster()` now warns when `quantile_by_key` is supplied with a quantile-output trainer (`quantile_reg()`, `rand_forest()` with engine `"grf_quantiles"`), where the argument would otherwise be silently ignored (#229).
- Regenerate roxygen-derived `man/step_adjust_latency.Rd` so its recorded example output matches the current `epi_df` print phrasing (`lag` → `latency`) from upstream `epiprocess`. No user-visible behavior change.
- Regenerate roxygen-derived `man/step_adjust_latency.Rd` so its recorded
example output matches the current `epi_df` print phrasing (`lag` → `latency`)
from upstream `epiprocess`. No user-visible behavior change.
- Bump GitHub action checkout version.

# epipredict 0.2.5

Expand Down
135 changes: 89 additions & 46 deletions R/arx_classifier.R
Original file line number Diff line number Diff line change
Expand Up @@ -141,21 +141,33 @@
#' )
#' )
arx_classifier <- function(
epi_data,
outcome,
predictors,
trainer = logistic_reg(),
args_list = arx_class_args_list()) {
epi_data,
outcome,
predictors,
trainer = logistic_reg(),
args_list = arx_class_args_list()
) {
if (!is_classification(trainer)) {
cli_abort("`trainer` must be a {.pkg parsnip} model of mode 'classification'.")
cli_abort(
"`trainer` must be a {.pkg parsnip} model of mode 'classification'."
)
}

wf <- arx_class_epi_workflow(epi_data, outcome, predictors, trainer, args_list)
wf <- arx_class_epi_workflow(
epi_data,
outcome,
predictors,
trainer,
args_list
)
wf <- fit(wf, epi_data)

if (args_list$adjust_latency == "none") {
forecast_date_default <- max(epi_data$time_value)
if (!is.null(args_list$forecast_date) && args_list$forecast_date != forecast_date_default) {
if (
!is.null(args_list$forecast_date) &&
args_list$forecast_date != forecast_date_default
) {
cli_warn(
"The specified forecast date {args_list$forecast_date} doesn't match the
date from which the forecast is occurring {forecast_date}."
Expand Down Expand Up @@ -218,23 +230,31 @@ arx_classifier <- function(
#' )
#' )
arx_class_epi_workflow <- function(
epi_data,
outcome,
predictors,
trainer = parsnip::logistic_reg(),
args_list = arx_class_args_list()) {
epi_data,
outcome,
predictors,
trainer = parsnip::logistic_reg(),
args_list = arx_class_args_list()
) {
validate_forecaster_inputs(epi_data, outcome, predictors)
if (!inherits(args_list, c("arx_class", "alist"))) {
cli_abort("`args_list` was not created using `arx_class_args_list()`.")
}
if (!(is.null(trainer) || is_classification(trainer))) {
cli_abort("`trainer` must be a {.pkg parsnip} model of mode 'classification'.")
cli_abort(
"`trainer` must be a {.pkg parsnip} model of mode 'classification'."
)
}

if (args_list$adjust_latency == "none") {
forecast_date_default <- max(epi_data$time_value)
if (!is.null(args_list$forecast_date) && args_list$forecast_date != forecast_date_default) {
cli_warn("The specified forecast date {args_list$forecast_date} doesn't match the date from which the forecast is occurring {forecast_date}.")
if (
!is.null(args_list$forecast_date) &&
args_list$forecast_date != forecast_date_default
) {
cli_warn(
"The specified forecast date {args_list$forecast_date} doesn't match the date from which the forecast is occurring {forecast_date}."
)
}
} else {
forecast_date_default <- attributes(epi_data)$metadata$as_of
Expand All @@ -257,7 +277,8 @@ arx_class_epi_workflow <- function(
for (l in seq_along(lags)) {
pred_names <- predictors[l]
pred_names <- as.character(glue::glue_data(
args_list, "gr_{horizon}_{method}_{pred_names}"
args_list,
"gr_{horizon}_{method}_{pred_names}"
))
r <- step_epi_lag(r, !!pred_names, lag = lags[[l]])
}
Expand Down Expand Up @@ -293,17 +314,24 @@ arx_class_epi_workflow <- function(
method_adjust_latency <- args_list$adjust_latency
if (method_adjust_latency != "none") {
if (method_adjust_latency != "extend_ahead") {
cli_abort("only extend_ahead is currently supported",
cli_abort(
"only extend_ahead is currently supported",
class = "epipredict__arx_classifier__adjust_latency_unsupported_method"
)
}
r <- r %>% step_adjust_latency(!!pre_out_name,
fixed_forecast_date = forecast_date,
method = method_adjust_latency
)
r <- r %>%
step_adjust_latency(
!!pre_out_name,
fixed_forecast_date = forecast_date,
method = method_adjust_latency
)
}
r <- r %>%
step_epi_ahead(!!pre_out_name, ahead = args_list$ahead, role = "pre-outcome")
step_epi_ahead(
!!pre_out_name,
ahead = args_list$ahead,
role = "pre-outcome"
)
r <- r %>%
step_mutate(
across(
Expand Down Expand Up @@ -345,8 +373,8 @@ arx_class_epi_workflow <- function(
#' be created using growth rates (as the predictors are) or lagged
#' differences. The second case is closer to the requirements for the
#' [2022-23 CDC Flusight Hospitalization Experimental Target](https://github.com/cdcepi/Flusight-forecast-data/blob/745511c436923e1dc201dea0f4181f21a8217b52/data-experimental/README.md).
#' See the [Classification chapter from the forecasting book](https://cmu-delphi.github.io/delphi-tooling-book/arx-classifier.html) Vignette for details of how to create a reasonable
#' baseline for this case. Selecting `"growth_rate"` (the default) uses
#' See the `vignette("epipredict")` for more details.
#' Selecting `"growth_rate"` (the default) uses
#' [epiprocess::growth_rate()] to create the outcome using some of the
#' additional arguments below. Choosing `"lag_difference"` instead simply
#' uses the change from the value at the selected `horizon`.
Expand Down Expand Up @@ -384,36 +412,48 @@ arx_class_epi_workflow <- function(
#' # also needs arx_classifier(trainer = parsnip::multinom_reg())
#' arx_class_args_list(breaks = c(-.2, .25))
arx_class_args_list <- function(
lags = c(0L, 7L, 14L),
ahead = 7L,
n_training = Inf,
forecast_date = NULL,
target_date = NULL,
adjust_latency = c("none", "extend_ahead", "extend_lags", "locf"),
warn_latency = TRUE,
outcome_transform = c("growth_rate", "lag_difference"),
breaks = 0.25,
horizon = 7L,
method = c("rel_change", "linear_reg"),
log_scale = FALSE,
check_enough_data_n = NULL,
check_enough_data_epi_keys = NULL,
...) {
lags = c(0L, 7L, 14L),
ahead = 7L,
n_training = Inf,
forecast_date = NULL,
target_date = NULL,
adjust_latency = c("none", "extend_ahead", "extend_lags", "locf"),
warn_latency = TRUE,
outcome_transform = c("growth_rate", "lag_difference"),
breaks = 0.25,
horizon = 7L,
method = c("rel_change", "linear_reg"),
log_scale = FALSE,
check_enough_data_n = NULL,
check_enough_data_epi_keys = NULL,
...
) {
rlang::check_dots_empty()
.lags <- lags
if (is.list(lags)) lags <- unlist(lags)
if (is.list(lags)) {
lags <- unlist(lags)
}
method <- rlang::arg_match(method)
outcome_transform <- rlang::arg_match(outcome_transform)

adjust_latency <- rlang::arg_match(adjust_latency)
arg_is_scalar(ahead, n_training, horizon, log_scale, adjust_latency, warn_latency)
arg_is_scalar(
ahead,
n_training,
horizon,
log_scale,
adjust_latency,
warn_latency
)
arg_is_scalar(forecast_date, target_date, allow_null = TRUE)
arg_is_date(forecast_date, target_date, allow_null = TRUE)
arg_is_nonneg_int(ahead, lags, horizon)
arg_is_numeric(breaks)
arg_is_lgl(log_scale)
arg_is_pos(n_training)
if (is.finite(n_training)) arg_is_pos_int(n_training)
if (is.finite(n_training)) {
arg_is_pos_int(n_training)
}
arg_is_pos(check_enough_data_n, allow_null = TRUE)
arg_is_chr(check_enough_data_epi_keys, allow_null = TRUE)

Expand All @@ -428,9 +468,12 @@ arx_class_args_list <- function(
}

breaks <- sort(breaks)
if (min(breaks) > -Inf) breaks <- c(-Inf, breaks)
if (max(breaks) < Inf) breaks <- c(breaks, Inf)

if (min(breaks) > -Inf) {
breaks <- c(-Inf, breaks)
}
if (max(breaks) < Inf) {
breaks <- c(breaks, Inf)
}

max_lags <- max(lags)
structure(
Expand Down
Loading