Skip to content

Commit 7bf677b

Browse files
committed
feat: add forecast method #293
1 parent 6b488a4 commit 7bf677b

File tree

5 files changed

+138
-49
lines changed

5 files changed

+138
-49
lines changed

NAMESPACE

+1
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ export(flatline)
152152
export(flatline_args_list)
153153
export(flatline_forecaster)
154154
export(flusight_hub_formatter)
155+
export(forecast)
155156
export(frosting)
156157
export(get_test_data)
157158
export(grab_names)

R/epi_workflow.R

+39-1
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,11 @@ update_model.epi_workflow <- function(x, spec, ..., formula = NULL) {
197197
#'
198198
#' @export
199199
fit.epi_workflow <- function(object, data, ..., control = workflows::control_workflow()) {
200-
object$fit$meta <- list(max_time_value = max(data$time_value), as_of = attributes(data)$metadata$as_of)
200+
object$fit$meta <- list(
201+
max_time_value = max(data$time_value),
202+
as_of = attributes(data)$metadata$as_of,
203+
train_data = data
204+
)
201205

202206
NextMethod()
203207
}
@@ -326,3 +330,37 @@ print.epi_workflow <- function(x, ...) {
326330
print_postprocessor(x)
327331
invisible(x)
328332
}
333+
334+
335+
#' Produce a forecast from an epi workflow
336+
#'
337+
#' @param epi_workflow An epi workflow
338+
#' @param fill_locf Logical. Should we use locf to fill in missing data?
339+
#' @param n_recent Integer or NULL. If filling missing data with locf = TRUE,
340+
#' how far back are we willing to tolerate missing data? Larger values allow
341+
#' more filling. The default NULL will determine this from the the recipe. For
342+
#' example, suppose n_recent = 3, then if the 3 most recent observations in any
343+
#' geo_value are all NA’s, we won’t be able to fill anything, and an error
344+
#' message will be thrown. (See details.)
345+
#' @param forecast_date By default, this is set to the maximum time_value in x.
346+
#' But if there is data latency such that recent NA's should be filled, this may
347+
#' be after the last available time_value.
348+
#'
349+
#' @return A forecast tibble.
350+
#'
351+
#' @export
352+
forecast <- function(epi_workflow, fill_locf = FALSE, n_recent = NULL, forecast_date = NULL) {
353+
if (!epi_workflow$trained) {
354+
cli_abort("The epi_workflow is not trained.")
355+
}
356+
357+
test_data <- get_test_data(
358+
hardhat::extract_preprocessor(epi_workflow),
359+
epi_workflow$fit$meta$train_data,
360+
fill_locf = fill_locf,
361+
n_recent = n_recent %||% Inf,
362+
forecast_date = forecast_date %||% max(epi_workflow$fit$meta$train_data$time_value)
363+
)
364+
365+
predict(epi_workflow, new_data = test_data)
366+
}

_pkgdown.yml

+44-48
Original file line numberDiff line numberDiff line change
@@ -30,81 +30,77 @@ repo:
3030

3131
home:
3232
links:
33-
- text: Introduction to Delphi's Tooling Work
34-
href: https://cmu-delphi.github.io/delphi-tooling-book/
35-
- text: The epiprocess R package
36-
href: https://cmu-delphi.github.io/epiprocess/
37-
- text: The epidatr R package
38-
href: https://github.com/cmu-delphi/epidatr/
39-
- text: The epidatasets R package
40-
href: https://cmu-delphi.github.io/epidatasets/
41-
- text: The covidcast R package
42-
href: https://cmu-delphi.github.io/covidcast/covidcastR/
43-
44-
33+
- text: Introduction to Delphi's Tooling Work
34+
href: https://cmu-delphi.github.io/delphi-tooling-book/
35+
- text: The epiprocess R package
36+
href: https://cmu-delphi.github.io/epiprocess/
37+
- text: The epidatr R package
38+
href: https://github.com/cmu-delphi/epidatr/
39+
- text: The epidatasets R package
40+
href: https://cmu-delphi.github.io/epidatasets/
41+
- text: The covidcast R package
42+
href: https://cmu-delphi.github.io/covidcast/covidcastR/
4543

4644
reference:
4745
- title: Simple forecasters
4846
desc: Complete forecasters that produce reasonable baselines
4947
contents:
50-
- contains("forecaster")
51-
- contains("classifier")
48+
- contains("forecaster")
49+
- contains("classifier")
5250
- title: Forecaster modifications
5351
desc: Constructors to modify forecaster arguments and utilities to produce `epi_workflow` objects
5452
contents:
55-
- contains("args_list")
56-
- contains("_epi_workflow")
53+
- contains("args_list")
54+
- contains("_epi_workflow")
5755
- title: Helper functions for Hub submission
5856
contents:
59-
- flusight_hub_formatter
57+
- flusight_hub_formatter
6058
- title: Parsnip engines
6159
desc: Prediction methods not available elsewhere
6260
contents:
63-
- quantile_reg
64-
- smooth_quantile_reg
61+
- quantile_reg
62+
- smooth_quantile_reg
6563
- title: Custom panel data forecasting workflows
6664
contents:
67-
- epi_recipe
68-
- epi_workflow
69-
- add_epi_recipe
70-
- adjust_epi_recipe
71-
- add_model
72-
- predict.epi_workflow
73-
- fit.epi_workflow
74-
- augment.epi_workflow
65+
- epi_recipe
66+
- epi_workflow
67+
- add_epi_recipe
68+
- adjust_epi_recipe
69+
- add_model
70+
- predict.epi_workflow
71+
- fit.epi_workflow
72+
- augment.epi_workflow
73+
- forecast
7574
- title: Epi recipe preprocessing steps
7675
contents:
77-
- starts_with("step_")
78-
- contains("bake")
79-
- contains("juice")
76+
- starts_with("step_")
77+
- contains("bake")
78+
- contains("juice")
8079
- title: Epi recipe verification checks
8180
contents:
82-
- check_enough_train_data
81+
- check_enough_train_data
8382
- title: Forecast postprocessing
8483
desc: Create a series of postprocessing operations
8584
contents:
86-
- frosting
87-
- ends_with("_frosting")
88-
- get_test_data
89-
- tidy.frosting
85+
- frosting
86+
- ends_with("_frosting")
87+
- get_test_data
88+
- tidy.frosting
9089
- title: Frosting layers
9190
contents:
92-
- contains("layer")
93-
- contains("slather")
91+
- contains("layer")
92+
- contains("slather")
9493
- title: Automatic forecast visualization
9594
contents:
96-
- autoplot.epi_workflow
97-
- autoplot.canned_epipred
95+
- autoplot.epi_workflow
96+
- autoplot.canned_epipred
9897
- title: Utilities for quantile distribution processing
9998
contents:
100-
- dist_quantiles
101-
- extrapolate_quantiles
102-
- nested_quantiles
103-
- starts_with("pivot_quantiles")
99+
- dist_quantiles
100+
- extrapolate_quantiles
101+
- nested_quantiles
102+
- starts_with("pivot_quantiles")
104103
- title: Included datasets
105104
contents:
106-
- case_death_rate_subset
107-
- state_census
108-
109-
110-
105+
- case_death_rate_subset
106+
- state_census

man/forecast.Rd

+35
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-forecast.R

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
test_that("forecast method works", {
2+
jhu <- case_death_rate_subset %>%
3+
filter(time_value > "2021-11-01", geo_value %in% c("ak", "ca", "ny"))
4+
r <- epi_recipe(jhu) %>%
5+
step_epi_lag(death_rate, lag = c(0, 7, 14)) %>%
6+
step_epi_ahead(death_rate, ahead = 7) %>%
7+
step_epi_naomit()
8+
wf <- epi_workflow(r, parsnip::linear_reg()) %>% fit(jhu)
9+
10+
latest <- get_test_data(
11+
hardhat::extract_preprocessor(wf),
12+
jhu
13+
)
14+
15+
expect_equal(
16+
forecast(wf),
17+
predict(wf, new_data = latest)
18+
)
19+
})

0 commit comments

Comments
 (0)