Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
835cafc
fix(autoplot.epi_workflow): solved error that made it use the .respon…
JavierMtzRdz Apr 24, 2026
2afcd8f
refactor: remove comment regarding facet separator alignment in autop…
JavierMtzRdz Apr 24, 2026
b5c8b19
fix: prevent using other subsamples.
JavierMtzRdz Apr 24, 2026
e70d341
chore: update NEWS with autoplot fix and update docs
JavierMtzRdz Apr 24, 2026
b0649e7
chore: bump version number
JavierMtzRdz Apr 24, 2026
92b3156
fix: accept snapshot with changes to the underlying data structure of…
JavierMtzRdz Apr 24, 2026
e2a4160
chore: update documentation, clean up NAMESPACE imports, and update c…
JavierMtzRdz Apr 24, 2026
becef19
refactor: update R/layers.R is_layer example basen on Logans suggestion
JavierMtzRdz Apr 24, 2026
f87d5cc
style(autoplot): style
JavierMtzRdz Apr 24, 2026
e38dc92
chore: remove deprecated .max_facets and update documentation
JavierMtzRdz Apr 24, 2026
de35d5e
Merge pull request #476 from cmu-delphi/jmr/fix-autoplot-rep-call
JavierMtzRdz Apr 24, 2026
859a9a1
fix: display latency adjustment info on canned forecasters (#447)
gregfaletto May 5, 2026
3293dd6
doc: version and news
dshemetov May 6, 2026
261af37
Merge pull request #477 from gregfaletto/fix-print-canned-pre-447
dsweber2 May 7, 2026
79945ad
fix: flatline_forecaster duplicate predictions on trailing-NA input (…
gregfaletto May 7, 2026
16acf01
Merge pull request #478 from gregfaletto/fix-flatline-na-454
dajmcdon May 8, 2026
6e253b9
fix: arx_forecaster cli template error on inconsistent target_date (#…
gregfaletto May 8, 2026
fb10996
Merge pull request #479 from gregfaletto/fix-cli-target-date-473
dajmcdon May 9, 2026
5698362
chore: regenerate roxygen-derived man/step_adjust_latency.Rd
gregfaletto May 9, 2026
c4fd110
fix: validate quantile_by_key in arx_forecaster and flatline_forecast…
gregfaletto May 20, 2026
470fefc
chore: switch version bump from 0.2.7 to 0.2.6
gregfaletto May 20, 2026
fc03d85
Merge pull request #480 from gregfaletto/regen-roxygen-docs
dajmcdon Jun 30, 2026
ea1649d
Merge branch 'dev' into fix-flatline-quantile-by-key-229
dajmcdon Jun 30, 2026
727cf0c
upd: github checkout to v6
dajmcdon Jun 30, 2026
f1c85c4
Merge pull request #481 from gregfaletto/fix-flatline-quantile-by-key…
dajmcdon Jun 30, 2026
9057482
Merge branch 'dev' into djm/summer-cleaning
dajmcdon Jun 30, 2026
e5f9cad
redocument
dajmcdon Jun 30, 2026
5f50233
Merge pull request #460 from cmu-delphi/ds/pivot_longer
dajmcdon Jun 30, 2026
a0d8782
Merge branch 'dev' into djm/summer-cleaning
dajmcdon Jun 30, 2026
80f1162
add news
dajmcdon Jul 1, 2026
e49e322
remove epidatr calls in readme
dajmcdon Jul 1, 2026
7847982
redocument
dajmcdon Jul 1, 2026
def8366
rebuilt readme figs
dajmcdon Jul 1, 2026
4f35a0e
don't cache readme outputs, rebuild
dajmcdon Jul 1, 2026
6db7649
fix broken links, air formatter
dajmcdon Jul 1, 2026
c7cbbb2
epiprocess depreceted autoplot.epi_df(.max_facets) arg. removed from …
dajmcdon Jul 1, 2026
150583d
update news, bump version
dajmcdon Jul 1, 2026
f7b83c7
missed one .max_keys
dajmcdon Jul 1, 2026
fbb758d
fix: update epiprocess dependency to dev branch and disable subsampli…
JavierMtzRdz Jul 2, 2026
b363a49
test: add unit tests for autoplot functionality with single and multi…
JavierMtzRdz Jul 2, 2026
12a19d9
chore: update epiprocess dependency to version 0.12.0 and remove dev …
JavierMtzRdz Jul 2, 2026
cf5aece
chore: update epiprocess dependency to version 0.13.0 or higher
JavierMtzRdz Jul 20, 2026
da0d3ed
refactor: update pivot_quantiles to register new quantile_level colum…
JavierMtzRdz Jul 20, 2026
ef1b4f8
Merge pull request #483 from cmu-delphi/jmr/max_keys-fix
dajmcdon Jul 20, 2026
43b352f
Merge pull request #482 from cmu-delphi/djm/summer-cleaning
dajmcdon Jul 20, 2026
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
3 changes: 3 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@
^.lintr$
^.venv$
^inst/templates$
^\.workflow$
^\.plans$
^\.claude$
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ inst/doc
renv.lock
renv/
.Renviron
.workflow/
.plans/
.claude/
8 changes: 4 additions & 4 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.1
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 Expand Up @@ -78,4 +78,4 @@ Config/Needs/website: cmu-delphi/delphidocs
Config/testthat/edition: 3
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.2
RoxygenNote: 7.3.3
39 changes: 37 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,39 @@

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.
- Bump GitHub action checkout version.

# epipredict 0.2.5

- Fix `arx_forecaster()` and `arx_fcast_epi_workflow()` so that the error raised when `forecast_date + ahead != target_date` reports the actual validation message rather than a cryptic `cli` template-evaluation error (#473).

# epipredict 0.2.4

- Fix `flatline_forecaster()` to return one prediction per geographic key when the input `epi_df` has trailing rows with `NA`s in the outcome (#454). Previously, the forecast was duplicated once per trailing-NA day.

# epipredict 0.2.3

- Fix `print.canned_epipred()` so the latency-adjustment information actually displays for canned forecasters that include `step_adjust_latency` in their recipe (#447).

# epipredict 0.2.2

- Fix `autoplot.epi_workflow()` to correctly handle the response variable and avoid errors related to `.response`.
- Prevent subsampling in `autoplot.epi_workflow()` by setting `.max_keys = Inf`.

# epipredict 0.2.1

- Fix bug in `flusight_hub_formatter()` so that it works as expected even if the user has not first loaded the `epidatasets` package.
Expand All @@ -16,12 +49,12 @@ Pre-1.0.0 numbering scheme: 0.x will indicate releases, while 0.0.x will indicat
`data(<dataset name>, package = "epidatasets")`, `epidatasets::<dataset name>`
or, after loading the package, the name of the dataset alone (#382).
- `step_adjust_latency()` no longer allows empty column selection.
- Addresses upstream breaking changes from cmu-delphi/epiprocess#595 (`growth_rate()`).
- Addresses upstream breaking changes from cmu-delphi/epiprocess#595 (`growth_rate()`).
`step_growth_rate()` has lost its `additional_gr_args_list` argument and now
has an `na_rm` argument.
- Moves `epiprocess` out of depends (#440). No internals have changed, but downstream
users may need to add `library(epiprocess)` to existing code.
- Removes dependence on the `distributional` package, replacing the quantiles
- Removes dependence on the `distributional` package, replacing the quantiles
with `hardhat::quantile_pred()`. Some associated functions are deprecated with
`lifecycle` messages.
- Rename `check_enough_train_data()` to `check_enough_data()`, and generalize it
Expand All @@ -43,6 +76,8 @@ Pre-1.0.0 numbering scheme: 0.x will indicate releases, while 0.0.x will indicat
- Allow `quantile()` to threshold to an interval if desired (#434)
- `arx_forecaster()` detects if there's enough data to predict
- Add `observed_response` to `autoplot` so that forecasts can be plotted against the values they're predicting
- `pivot_quantiles_longer()` now appropriately adds `quantile_level` to the
`epi_df` other keys

## Bug fixes

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
Loading