# Shared by the experiment notebooks: hub submissions, finalized truth for
# scoring, NHSN vintages for learning, and population for rates. The disease
# comes from `params$disease` (default flu).
suppressPackageStartupMessages(source(here::here("R/load_all.R")))
disease <- params$disease %||% "flu"
archive <- get_nhsn_data_archive(disease)
truth <- nhsn_read_truth(disease, archive)
vintages <- nhsn_read_vintages(disease, archive)
hub_target <- c(flu = HUB_FLU_TARGET, covid = HUB_COVID_TARGET)[[disease]]
hub_fc <- suppressMessages(hub_read_forecasts(hub_dir = here::here(params$hub_dir), target = hub_target))
population <- get_population_data() %>%
distinct(state_code, .keep_all = TRUE) %>%
filter(state_code %in% unique(hub_fc$location)) %>%
select(location = state_code, population)
rate_scales <- population %>% transmute(location, from = as.Date("2000-01-01"), scale = population / 1e5)
LIVE <- c("2024-2025", "2025-2026")
run <- function(...) cal_run_cached(hub_fc, truth, ..., vintages = vintages, workers = params$workers)
Question: does the scale the tracker works on (count, sqrt, rate) change WIS and coverage?
Setup:
settle_days = 14).| config | change from REF-paper |
|---|---|
count |
none (additive offsets in admissions) |
sqrt |
offsets on the square root of admissions, mapped back; learning-rate floor 0.001 |
rate |
offsets in admissions per 100k; learning-rate floor 0.001 |
The learning-rate floor (0.1 by default) is in the tracker’s working units, so the sqrt and rate configs use 0.001.
cals <- list(
count = run("paper"),
sqrt = run("paper", transform = "sqrt", lr_args = list(mult = 0.03, floor = 1e-3)),
rate = run("paper", scales = rate_scales, lr_args = list(mult = 0.03, floor = 1e-3))
)
mv <- cal_month_view(cals, seasons = LIVE, horizons = NULL)
Scores by calendar month of the reference date, one figure per season, states only (US excluded). Rows are the three metrics, columns the horizons:
100 × (WIS_base − WIS_cal) / WIS_base within the month, so
positive = calibrated WIS lower than base. Grey bars are the month’s
share of the season’s base WIS, so a large swing in a month with a small
bar costs little overall.100 × (bias_base − bias_cal) / bias_base, so positive =
calibrated coverage bias lower than base. The bias is computed within
the month: the mean over the 23 levels of |share of outcomes at or below
the quantile − level|. A season-pooled bias lets under-prediction on the
ramp cancel over-prediction after the peak; the monthly one does not.
When the base’s bias is already small, a small absolute change shows as
a large percentage; the month-average column of the coverage table below
gives the absolute values.A month holds one to five rounds of about 52 states, and outcomes in the same round move together, so a single month’s coverage bias is noisy and sits above zero even for a calibrated forecaster. That’s why the plot shows it relative to the base.
mv_plot_month <- mv$month
if (!is.null(get0("mv_plot_cfgs"))) {
mv_plot_month <- mv_plot_month %>% filter(config %in% mv_plot_cfgs) %>% mutate(config = droplevels(config))
}
for (s in sort(unique(mv_plot_month$season))) {
cat("\n\n", cal_h(2), " ", s, "\n\n", sep = "")
print(cal_month_plot(mv_plot_month, s, colours = get0("mv_cols"), linetype = get0("mv_linetype"), colour_by = get0("mv_colour_by") %||% "config"))
}
cat("\n\n", cal_h(2), " By horizon\n\n", sep = "")
cat("WIS reduction % (positive = calibrated WIS lower than base), states only; `both` pools the seasons.\n\n")
WIS reduction % (positive = calibrated WIS lower than base), states
only; both pools the seasons.
print(knitr::kable(cal_month_wis_table(mv)))
| config | 2024-2025 h-1 | 2024-2025 h0 | 2024-2025 h1 | 2024-2025 h2 | 2024-2025 h3 | 2025-2026 h-1 | 2025-2026 h0 | 2025-2026 h1 | 2025-2026 h2 | 2025-2026 h3 | both h-1 | both h0 | both h1 | both h2 | both h3 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | +4.6 | +1.6 | +0.5 | -1.5 | -4.5 | -2.0 | -1.7 | -4.9 | -5.6 | -6.5 | +1.9 | +0.6 | -1.5 | -3.1 | -5.3 |
| sqrt | +8.1 | +2.1 | +1.1 | -0.2 | -1.4 | -1.1 | +2.6 | -2.0 | -3.1 | -2.5 | +4.3 | +2.3 | -0.0 | -1.3 | -1.8 |
| rate | +4.6 | +1.6 | +0.5 | -1.5 | -4.5 | -2.0 | -1.7 | -4.9 | -5.6 | -6.5 | +1.9 | +0.6 | -1.5 | -3.1 | -5.3 |
print(knitr::kable(cal_month_cov_table(mv), caption = CAL_MONTH_COV_CAPTION))
| config | 2024-2025 h-1 | 2024-2025 h0 | 2024-2025 h1 | 2024-2025 h2 | 2024-2025 h3 | 2025-2026 h-1 | 2025-2026 h0 | 2025-2026 h1 | 2025-2026 h2 | 2025-2026 h3 | both h-1 | both h0 | both h1 | both h2 | both h3 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| base | 0.183 / 0.191 | 0.131 / 0.146 | 0.135 / 0.152 | 0.146 / 0.169 | 0.159 / 0.190 | 0.037 / 0.084 | 0.078 / 0.089 | 0.055 / 0.098 | 0.050 / 0.108 | 0.046 / 0.117 | 0.110 / 0.134 | 0.104 / 0.116 | 0.094 / 0.123 | 0.097 / 0.137 | 0.101 / 0.151 |
| count | 0.100 / 0.160 | 0.057 / 0.122 | 0.050 / 0.135 | 0.042 / 0.172 | 0.046 / 0.223 | 0.013 / 0.076 | 0.008 / 0.085 | 0.018 / 0.106 | 0.028 / 0.118 | 0.040 / 0.120 | 0.044 / 0.115 | 0.030 / 0.102 | 0.032 / 0.120 | 0.033 / 0.143 | 0.031 / 0.168 |
| sqrt | 0.123 / 0.149 | 0.091 / 0.118 | 0.089 / 0.122 | 0.089 / 0.143 | 0.082 / 0.181 | 0.027 / 0.072 | 0.034 / 0.089 | 0.038 / 0.113 | 0.034 / 0.118 | 0.030 / 0.122 | 0.049 / 0.108 | 0.032 / 0.102 | 0.026 / 0.117 | 0.028 / 0.130 | 0.026 / 0.150 |
| rate | 0.100 / 0.160 | 0.057 / 0.122 | 0.050 / 0.135 | 0.042 / 0.172 | 0.046 / 0.223 | 0.013 / 0.076 | 0.008 / 0.085 | 0.018 / 0.106 | 0.028 / 0.118 | 0.040 / 0.120 | 0.044 / 0.115 | 0.030 / 0.102 | 0.032 / 0.120 | 0.033 / 0.143 | 0.031 / 0.168 |