使用寓言包计算 R 中测试集的 1 步预测误差的正确方法是什么?

What is the correct way to calculate 1 step forecast errors over a test set in R using the fable package?

我正在努力理解使用 R 的 {fable} 包计算测试集上的一步预测误差的正确方法。

首先,我对单步预测误差的理解是:

  1. 在时间t,我们预测下一个时间段,t+1
  2. 观察t+1,计算残差等
  3. 根据我们在 t+1 观察到的新数据对 t+2 进行预测(使用我们在时间 t 所做的相同 model/coefs)
  4. 对测试集重复步骤 1-3。

我看过这个 post: which aligns with the method discussed in FPP3,但我得到的结果与我的直觉不符。当测试集小于拟合模型的季节性 window 时,此方法也会失败。此外,当没有 refit() 方法可用于预测算法时,您会怎么做? (例如提供的 THETA() 技术)。

下面是用 2 种不同方式截取的代码,我认为应该在拟合模型后计算 24m 测试集上的 1 步预测误差。第一个基于上面链接的 post 中的方法,第二个是我想出的循环。

这两种方法都产生了准确性和预测,但它们的差异并不小。他们为什么不同?哪一个是正确的?

一般来说,如果我有一个模型并对 t+1 进行预测,观察 y_{t+1},我并不清楚我将如何使用经过训练的模型和新的观察来进行预测对于 t+2。

# Init
library(tidyverse)
library(fable)
#> Loading required package: fabletools
    
# Prepare data
us_accidental_deaths <- as_tsibble(USAccDeaths)
deaths_train <- head(us_accidental_deaths, -24)
deaths_test <- tail(us_accidental_deaths, 24)

# Get models on training data
deaths_fits_0 <- deaths_train %>% 
    model(ets = ETS(value))

##############
# Normal way #
##############

# 'refit' without estimating new params/coefs on the new data
deaths_fits_1 <- deaths_fits_0 %>% 
    refit(deaths_test, reestimate = FALSE)  
    # what happens here if the test set is smaller than the seasonality windows?

# 1-step forecast accuracy on the test set?
deaths_fits_1 %>%
    accuracy()
#> # A tibble: 1 x 10
#>   .model .type       ME  RMSE   MAE   MPE  MAPE  MASE RMSSE   ACF1
#>   <chr>  <chr>    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl>
#> 1 ets    Training  30.4  142.  111. 0.334  1.24 0.428 0.416 -0.105

# The 1-step forecasts?
fitted(deaths_fits_1)
#> # A tsibble: 24 x 3 [1M]
#> # Key:       .model [1]
#>    .model    index .fitted
#>    <chr>     <mth>   <dbl>
#>  1 ets    1977 Jan   7770.
#>  2 ets    1977 Feb   6906.
#>  3 ets    1977 Mar   7680.
#>  4 ets    1977 Apr   8135.
#>  5 ets    1977 May   8964.
#>  6 ets    1977 Jun   9264.
#>  7 ets    1977 Jul  10457.
#>  8 ets    1977 Aug   9547.
#>  9 ets    1977 Sep   8520.
#> 10 ets    1977 Oct   8660.
#> # ... with 14 more rows

################
# Using a loop #
################

# Initialise fit
death_fits_2 <- deaths_fits_0
# List to store 1 step forecasts in
test_forecasts_list <- list()

# Begin loop
for (i in 1:length(unique(deaths_test$index))){
    # 1 step forecast
    one_step_fc <- death_fits_2 %>%
        forecast(h = 1)
    # store
    test_forecasts_list[[i]] <- one_step_fc
    # refit using the newly observed datapoint
    death_fits_2 <- deaths_fits_0 %>%
        refit(
            bind_rows(
                deaths_train,
                deaths_test %>% 
                    arrange(index) %>%
                    slice_head(n=i)
            ),
            reestimate=FALSE
        )
}
test_forecasts_2 <- bind_rows(test_forecasts_list)

# 1-step forecast accuracy over test set
test_forecasts_2 %>%
    accuracy(data=us_accidental_deaths) 
#> # A tibble: 1 x 10
#>   .model .type    ME  RMSE   MAE   MPE  MAPE  MASE RMSSE   ACF1
#>   <chr>  <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl>
#> 1 ets    Test   37.0  255.  194. 0.308  2.19 0.352 0.380 -0.305

# 1-step forecasts
test_forecasts_2
#> # A fable: 24 x 4 [1M]
#> # Key:     .model [1]
#>    .model    index           value  .mean
#>    <chr>     <mth>          <dist>  <dbl>
#>  1 ets    1977 Jan N(7837, 110265)  7837.
#>  2 ets    1977 Feb  N(7129, 95555)  7129.
#>  3 ets    1977 Mar  N(7783, 93292)  7783.
#>  4 ets    1977 Apr  N(7912, 91760)  7912.
#>  5 ets    1977 May  N(8894, 89411)  8894.
#>  6 ets    1977 Jun  N(9418, 87442)  9418.
#>  7 ets    1977 Jul N(10072, 85241) 10072.
#>  8 ets    1977 Aug  N(9891, 89234)  9891.
#>  9 ets    1977 Sep  N(8388, 93841)  8388.
#> 10 ets    1977 Oct  N(8679, 91768)  8679.
#> # ... with 14 more rows

reprex package (v2.0.1)

于 2022-05-31 创建

ETS模型中有两组参数,初始状态和平滑参数。使用原始初始状态并将它们应用于不同的时间段几乎没有任何意义,因此默认情况下 refit() re-estimates 初始状态但在提供的模型中保留平滑参数。

在您的第一次尝试中,初始状态在测试数据开始时为 re-estimated,而在您的循环中,初始状态在每次迭代中为 re-estimated。

您可能想要的不是 re-estimate 任何东西,而是查看您的模型在测试集上的表现如何。您可以通过将模型重新拟合到训练集和测试集的组合,然后过滤掉测试集上的结果来做到这一点。像这样:

# 'refit' without estimating new params/coefs on all data
deaths_fits_1 <- deaths_fits_0 %>% 
  refit(us_accidental_deaths, reinitialise=FALSE) 

# 1-step forecasts on the test set
deaths_fits_1 %>%
  fitted() %>%
  tail(24)

这也避免了当测试集小于季节性周期时产生错误的问题。