Skip to content

Commit 750d3fb

Browse files
authored
Merge pull request #406 from cmu-delphi/scorecaster
Scorecaster
2 parents 8b1e482 + bbd4b49 commit 750d3fb

File tree

1 file changed

+97
-0
lines changed

1 file changed

+97
-0
lines changed

vignettes/articles/scorecaster.Rmd

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
---
2+
title: "Implementing a scorecaster for quantile calibration"
3+
---
4+
5+
```{r setup, include = FALSE}
6+
knitr::opts_chunk$set(
7+
collapse = TRUE,
8+
comment = "#>",
9+
warning = FALSE,
10+
message = FALSE,
11+
cache = TRUE
12+
)
13+
```
14+
15+
16+
```{r packages}
17+
library(tidyverse)
18+
library(epipredict)
19+
```
20+
21+
First we get some forecasts.
22+
23+
```{r forecast-output}
24+
jhu <- case_death_rate_subset
25+
fc_time_values <- seq(as.Date("2021-03-09"), as.Date("2021-12-01"), by = "4 weeks")
26+
q_levels <- c(1, 2, 5, 8, 9) / 10
27+
forecaster <- function(x, aheads = 7) {
28+
map(aheads, ~ arx_forecaster(
29+
x, "death_rate", c("case_rate", "death_rate"),
30+
quantile_reg(quantile_levels = q_levels),
31+
arx_args_list(ahead = .x, quantile_levels = q_levels)
32+
)$predictions |>
33+
mutate(ahead = .x)
34+
) |> list_rbind()
35+
}
36+
37+
out <- map(
38+
.x = fc_time_values,
39+
.f = ~forecaster(jhu %>% filter(time_value <= .x), c(7, 14, 21, 28)),
40+
.progress = TRUE
41+
)
42+
43+
out <- out %>% list_rbind()
44+
out <- left_join(
45+
out,
46+
jhu,
47+
by = c("target_date" = "time_value", "geo_value")
48+
)
49+
```
50+
51+
Now we set up the "quantile conformal score" and the tangent integrator.
52+
53+
```{r necessary-funs}
54+
quantile_conformal_score <- function(x, actual) {
55+
UseMethod("quantile_conformal_score")
56+
}
57+
quantile_conformal_score.distribution <- function(x, actual) {
58+
l <- vctrs::vec_recycle_common(x = x, actual = actual)
59+
map2(
60+
.x = vctrs::vec_data(l$x),
61+
.y = l$actual,
62+
.f = quantile_conformal_score
63+
)
64+
}
65+
quantile_conformal_score.dist_quantiles <- function(x, actual) {
66+
values <- vctrs::field(x, "values")
67+
quantile_levels <- vctrs::field(x, "quantile_levels")
68+
errs <- (actual - values) * (quantile_levels > 0.5) +
69+
(values - actual) * (quantile_levels < 0.5) +
70+
abs(actual - values) * (quantile_levels == 0.5)
71+
errs
72+
}
73+
74+
tangent_integrator <- function(x, t, KI = 1000, Csat = 2) {
75+
# defaults from https://github.com/aangelopoulos/conformal-time-series/blob/b729c3f5ff633bfc43f0f7ca08199b549c2573ac/tests/configs/ca-COVID-deaths-4wk.yaml#L41
76+
x <- x * log(t + 1) / (Csat * (t + 1))
77+
up <- x >= pi / 2
78+
down <- x <= -pi / 2
79+
x[up] <- Inf
80+
x[down] <- -Inf
81+
mid <- !up & !down
82+
x[mid] <- KI * tan(x[mid])
83+
}
84+
```
85+
86+
Score the forecasts.
87+
88+
```{r score-fcasts}
89+
out <- out |>
90+
mutate(qc_scores = quantile_conformal_score(.pred_distn, death_rate))
91+
```
92+
93+
Now we would need a "scorecaster". The paper has code here:
94+
https://github.com/aangelopoulos/conformal-time-series/blob/b729c3f5ff633bfc43f0f7ca08199b549c2573ac/tests/datasets/covid-ts-proc/statewide/death-forecasting-perstate-lasso-qr.ipynb
95+
96+
Not quite sure what the model is. Note that `epipredict::quantile_reg()` may work
97+
(without the $\ell_1$ penalty).

0 commit comments

Comments
 (0)