Takes a tbl_now and chooses a model for you: it builds a grid of
candidate models (epidemic process x reporting-delay family) sized to how much
data you have, backtest()s them over several historical dates, score()s
them, keeps the best one, and refits it on the full data. The returned object
is an ordinary nowcast() result (so autoplot(), predict(), etc. work),
with the ranked scoreboard attached in its comparison slot.
Usage
auto_nowcast(
data,
metric = c("wis", "ape", "mse", "coverage", "coverage_50", "coverage_90"),
type = c("auto", "two_stage", "one_stage"),
sir = NULL,
ar = NULL,
hsgp = NULL,
delays = NULL,
likelihood = nb_likelihood(),
models = NULL,
n_dates = 6L,
n_draws_select = 500L,
n_draws = 2000L,
K = 25L,
K_select = 10L,
min_ar = 15L,
min_hsgp = 30L,
now = NULL,
seed = sample.int(.Machine$integer.max, 1),
verbose = TRUE,
...
)Arguments
- data
A
tbl_nowobject (tbl.now::tbl_now()).- metric
Selection criterion.
"wis"(default, lowest Weighted Interval Score),"ape"(lowest absolute percentage error of the median),"mse"(lowest mean squared error), or one of the calibration criteria, which pick the model whose empirical interval coverage is closest to nominal:"coverage_50"(smallest|0.50 - coverage_50|),"coverage_90"(smallest|0.90 - coverage_90|), or"coverage"(smallest|0.50 - coverage_50| + |0.90 - coverage_90|, i.e. both intervals jointly).- type
Stage strategy used for both the backtest and the final fit:
"auto"(default),"two_stage", or"one_stage"(seenowcast()).- sir, ar, hsgp
Optional epidemic-process components (e.g.
sir_epidemic(R0 = ...)) carrying your priors. If supplied, that process is forced into the candidate grid; otherwise the plain constructor is used when the series length calls for it.- delays
A list of delay components to compare. Default:
list(lognormal_delay(), generalized_gamma_delay(), dirichlet_delay()).- likelihood
Either a single likelihood used for every candidate (default
nb_likelihood()), or a list of likelihoods to compare too, e.g.list(nb_likelihood(), poisson_likelihood()).- models
Optional
model()object or list of them (e.g. carrying acustom_delay()/custom_epidemic()) appended to the candidate grid so they compete in the same backtest.- n_dates
Number of historical dates to backtest over (default 6).
- n_draws_select
Posterior draws during the selection backtest (default 500 – kept small for speed).
- n_draws
Posterior draws for the final fit of the winning model (default 2000).
- K
Delay imputations for the final two-stage fit of the winning model (default 25).
- K_select
Delay imputations during the selection backtest (default 10 – kept small for speed, like
n_draws_select). The selection backtest fits the whole grid over many dates, so its cost scales withK_select; ranking the candidates is robust to a coarser imputation than the final fit. Lower it (e.g.5) for a long series where selection dominates the runtime.- min_ar, min_hsgp
Series-length thresholds (in event-times) at which AR(1) and HSGP become candidates (defaults 15 and 30).
- now
As-of date for the final fit (default: the
tbl_now'snow).- seed
RNG seed.
- verbose
Print progress and the chosen model (default
TRUE).- ...
Passed through to
backtest()andnowcast()(e.g.temporal_effects).
Value
A nowcast_class (as from nowcast()) for the selected model, with
the model-selection scoreboard in its comparison slot:
list(scores, chosen, metric, max_time).
Details
Candidate epidemic processes are chosen by series length (max_time, the
number of event-times): a process becomes a candidate as soon as the series is
long enough to support it (SIR needs the least data, the HSGP the most) and is
never dropped for being too long, so the comparison always spans every
process the data can support. With the default thresholds:
max_time < min_ar-> compares{SIR};min_ar <= max_time < min_hsgp-> compares{SIR, AR(1)};max_time >= min_hsgp-> compares{SIR, AR(1), HSGP}.
Any process you pass explicitly via sir / ar / hsgp is always included
(regardless of length), which is how you make a prior compete: e.g. pass
sir = sir_epidemic(R0 = lognormal_prior(log(3), 0.2)) and the SIR candidate
will use that R0 prior throughout the comparison.
Robustness. A candidate that fails to converge on a backtest date simply
drops out of the comparison there (it never aborts the search), and candidates
are scored on the common set of dates where they all produced a forecast so a
model cannot "win" on a lucky subset. The winner is then refit on the full
data; if that refit fails, auto_nowcast() falls through to the next-best
candidate (and so on), so it converges whenever any candidate would.
Candidate delays default to LogNormal, Generalized-Gamma and Dirichlet;
override with delays.
Speed. The grid is backtested with a fast configuration
(n_draws_select posterior draws over n_dates dates spread across the
history); only the winning model is refit with the full n_draws. Backtesting
is the expensive step – set a future::plan() (e.g.
future::plan(multisession)) for parallel speed-up.
Examples
# \donttest{
library(tbl.now)
data(denguedat)
# A short window keeps this example quick (auto_nowcast fits a whole grid):
dn <- subset(denguedat,
onset_week >= as.Date("1990-06-01") & onset_week <= as.Date("1990-12-01"))
tn <- tbl_now(dn, event_date = onset_week, report_date = report_week,
data_type = "linelist", verbose = FALSE)
# Backtesting the grid is the expensive step -- uncomment to run candidates
# in parallel (then restore sequential afterwards):
# future::plan(future::multisession, workers = 4)
# Compare a couple of delays; make the SIR candidate use a custom R0 prior:
nc <- auto_nowcast(tn,
sir = sir_epidemic(R0 = lognormal_prior(log(2), 0.3)),
delays = list(lognormal_delay(), dirichlet_delay()),
n_dates = 2, n_draws_select = 150, n_draws = 300,
temporal_effects = "none")
#> ℹ auto_nowcast: comparing 6 candidate models (1 likelihood x 3 epidemic
#> processes x 2 delays) over 2 backtest dates; max_time = 35.
#> ℹ Running 12 backtest cells sequentially.
#> • For a large grid, set a parallel plan first:
#> `future::plan(future::multisession, workers = N)`.
#> ✔ auto_nowcast: selected HSGP/nb/LogNormal (best wis).
# future::plan(future::sequential)
best_model_name(nc) # the winning model's label
#> [1] "HSGP/nb/LogNormal"
comparison_scores(nc) # the ranked scoreboard
#> model wis overprediction underprediction dispersion
#> 1 HSGP/nb/LogNormal 18.60444 0 6.000000 12.604444
#> 2 SIR/nb/Dirichlet 21.49708 0 9.388889 12.108194
#> 3 HSGP/nb/Dirichlet 22.08194 0 13.333333 8.748611
#> 4 SIR/nb/LogNormal 22.12306 0 12.000000 10.123056
#> 5 AR1/nb/Dirichlet 25.44486 0 16.388889 9.055972
#> 6 AR1/nb/LogNormal 28.04625 0 20.500000 7.546250
#> coverage_50 coverage_90 ape mse n
#> 1 1 1 0.5346535 2916.00 1
#> 2 0 1 0.6386139 4160.25 1
#> 3 0 1 0.6138614 3844.00 1
#> 4 0 1 0.5643564 3249.00 1
#> 5 0 1 0.7772277 6162.25 1
#> 6 0 1 0.7970297 6480.25 1
best_score(nc) # just the winner's row
#> model wis overprediction underprediction dispersion
#> 1 HSGP/nb/LogNormal 18.60444 0 6 12.60444
#> coverage_50 coverage_90 ape mse n
#> 1 1 1 0.5346535 2916 1
selection_metric(nc) # which metric chose it
#> [1] "wis"
winner <- best_model(nc) # the model() object, to reuse elsewhere
# }
