-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rework add coverage to work with raw forecasts #426
Changes from all commits
32357f1
1be9055
8719dbe
1252e13
3af9fff
02c4b1c
b41d655
79fc533
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
#' @title Add Coverage Values to Quantile-Based Forecasts | ||
#' | ||
#' @description Adds interval coverage of central prediction intervals, | ||
#' quantile coverage for predictive quantiles, as well as the deviation between | ||
#' desired and actual coverage to a data.table. Forecasts should be in a | ||
#' quantile format (following the input requirements of `score()`). | ||
#' | ||
#' **Interval coverage** | ||
#' | ||
#' Coverage for a given interval range is defined as the proportion of | ||
#' observations that fall within the corresponding central prediction intervals. | ||
#' Central prediction intervals are symmetric around the median and and formed | ||
#' by two quantiles that denote the lower and upper bound. For example, the 50% | ||
#' central prediction interval is the interval between the 0.25 and 0.75 | ||
#' quantiles of the predictive distribution. | ||
#' | ||
#' The function `add_coverage()` computes the coverage per central prediction | ||
#' interval, so the coverage will always be either `TRUE` (observed value falls | ||
#' within the interval) or `FALSE` (observed value falls outside the interval). | ||
#' You can summarise the coverage values to get the proportion of observations | ||
#' that fall within the central prediction intervals. | ||
#' | ||
#' **Quantile coverage** | ||
#' | ||
#' Quantile coverage for a given quantile is defined as the proportion of | ||
#' observed values that are smaller than the corresponding predictive quantile. | ||
#' For example, the 0.5 quantile coverage is the proportion of observed values | ||
#' that are smaller than the 0.5 quantile of the predictive distribution. | ||
#' | ||
#' **Coverage deviation** | ||
#' | ||
#' The coverage deviation is the difference between the desired coverage and the | ||
#' actual coverage. For example, if the desired coverage is 90% and the actual | ||
#' coverage is 80%, the coverage deviation is -0.1. | ||
#' | ||
#' @inheritParams score | ||
#' @return a data.table with the input and columns "interval_coverage", | ||
#' "interval_coverage_deviation", "quantile_coverage", | ||
#' "quantile_coverage_deviation" added. | ||
#' @importFrom data.table setcolorder | ||
#' @examples | ||
#' library(magrittr) # pipe operator | ||
#' example_quantile %>% | ||
#' add_coverage() | ||
#' @export | ||
#' @keywords scoring | ||
#' @export | ||
add_coverage <- function(data) { | ||
stored_attributes <- get_scoringutils_attributes(data) | ||
data <- validate(data) | ||
forecast_unit <- get_forecast_unit(data) | ||
data_cols <- colnames(data) # store so we can reset column order later | ||
|
||
# what happens if quantiles are not symmetric around the median? | ||
# should things error? Also write tests for that. | ||
interval_data <- quantile_to_interval(data, format = "wide") | ||
interval_data[, interval_coverage := ifelse( | ||
observed <= upper & observed >= lower, | ||
TRUE, | ||
FALSE) | ||
][, c("lower", "upper", "observed") := NULL] | ||
|
||
data[, range := get_range_from_quantile(quantile)] | ||
|
||
data <- merge(interval_data, data, by = unique(c(forecast_unit, "range"))) | ||
data[, interval_coverage_deviation := interval_coverage - range / 100] | ||
data[, quantile_coverage := observed <= predicted] | ||
data[, quantile_coverage_deviation := quantile_coverage - quantile] | ||
|
||
# reset column order | ||
new_metrics <- c("interval_coverage", "interval_coverage_deviation", | ||
"quantile_coverage", "quantile_coverage_deviation") | ||
setcolorder(data, unique(c(data_cols, "range", new_metrics))) | ||
|
||
# add coverage "metrics" to list of stored metrics | ||
# this makes it possible to use `summarise_scores()` later on | ||
stored_attributes[["metric_names"]] <- c( | ||
stored_attributes[["metric_names"]], | ||
new_metrics | ||
) | ||
data <- assign_attributes(data, stored_attributes) | ||
return(data[]) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -91,8 +91,8 @@ Forecasts can be easily and quickly scored using the `score()` function. `score( | |
example_quantile %>% | ||
set_forecast_unit(c("location", "target_end_date", "target_type", "horizon", "model")) %>% | ||
validate() %>% | ||
add_coverage() %>% | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if its in the default metrics list what does this actually do? Is it going to be clear to users why it isn't part of score? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updating the readme is on the list: #439 |
||
score() %>% | ||
add_coverage(ranges = c(50, 90), by = c("model", "target_type")) %>% | ||
summarise_scores( | ||
by = c("model", "target_type") | ||
) %>% | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you use the metrices functions vs duplicating here to make the code cleaner?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the moment not really - the thing is that the metrics functions do a many-to-one mapping (i.e. you get one coverage value per forecast (which comprises several quantiles)), whereas this is one-to-one, i.e. one value per quantile / interval.
We need to think again about how to handle one-to-one functions that exist currently and what to do with them: #451