I have a few general questions about pinball loss:
Is a pinball loss typically calculated for each point in the forecast horizon or is it calculated across all points in the forecast horizon?
How is pinball loss typically calculated for a point forecast, which does not provide prediction quantiles?
Can someone provide a simple example or link to code for pinball loss. I have some sample code below, but it calculates loss at each point in the forecast horizon, maybe this is incorrect.
Formula for Pinball Loss
Pinball loss functional form I am using is.
$S_\tau(y, q) = 1_{y \geq q}\tau(y-q) + 1_{q > y}(1 - \tau)(q - y) = (y-q)(\tau - 1_{y - q < 0})$
Where $q$ is the quantile forecast, $y$ is the observed actual, $\tau$ is the quantile level ($\tau \in Q$ where $Q$ is the quantiles $[0.1, 0.2, \dots, 0.9]$), and $1$ is the indicator function. (Quantile regression for the qualifying match of GEFCom2017 probabilistic load forecasting)
pb_score <- function(y, q, tau){
# y = actual value; q = quantile forecasted value; tau = quantile level
indicator <- ifelse(y - q < 0, 1, 0)
score <- (y - q) * (tau - indicator)
return(score)
}
Sample of pinball loss calculation
# calculate the pinball loss for 1 series in the syph data set
# pinball loss is calculated at each point in the forecast
# horizon.
require(tidyverse) # for data manipulation
require(ZIM) # for syph data
require(forecast) # forcast package
require(janitor) # clean names
data(syph)
# prep data
syph_ts <- ts(syph[,-1:-2], frequency = 52)
set.seed(42)
# ets
ets_fc <- function(ts_data, h){
f = janitor::clean_names(as_tibble(forecast(train, h = h, level = seq(60,90,10))))
horizon <- seq(1:h)
cbind(horizon, f)
}
train <- subset(syph_ts[, 1],end=length(syph_ts[, 1])-9)
test <- subset(syph_ts[, 1],start=length(syph_ts[, 1])-8)
h <- length(test)
ets_fcast <- ets_fc(train, h = h)
# pinball loss
pb_score <- function(y, q, tau){
# y = actual value; q = quantile forecasted value; tau = quantile level
indicator <- ifelse(y - q < 0, 1, 0)
score <- (y - q) * (tau - indicator)
return(score)
}
y <- test
# ets pinball score
for(i in 1:h){
ets_score <- rowMeans(rbind(
pb_score(y = y[i], ets_fcast[i, ]$lo_90, tau = .1),
pb_score(y = y[i], ets_fcast[i, ]$lo_80, tau = .2),
pb_score(y = y[i], ets_fcast[i, ]$lo_70, tau = .3),
pb_score(y = y[i], ets_fcast[i, ]$lo_60, tau = .4),
pb_score(y = y[i], ets_fcast[i, ]$point_forecast, tau = .5),
pb_score(y = y[i], ets_fcast[i, ]$hi_60, tau = .6),
pb_score(y = y[i], ets_fcast[i, ]$hi_70, tau = .7),
pb_score(y = y[i], ets_fcast[i, ]$hi_80, tau = .8),
pb_score(y = y[i], ets_fcast[i, ]$hi_90, tau = .9)
))
}