tidyverts / tidyverts/fable

No supported way to reuse fixed ARIMA orders with regressors under refitting / CV

Open
#443 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
R
Stars
588
Forks
71
Avg merge
1h 58m
Merged PRs (30d)
1

Description

TL;DR:

fable allows extracting ARIMA orders programmatically but provides no supported, reliable way to reapply those orders when refitting models with regressors (e.g., under expanding‑window CV), since pdq()/PDQ() work only as inline syntax and are not stable for reuse.

Title

No supported way to reuse fixed ARIMA orders with regressors under refitting / CV

Body

Summary

I am trying to fit ARIMA‐with‐regression models where the ARIMA orders are identified once and then reused across refits (e.g., expanding‑window CV or production retraining). While fable allows programmatic extraction of ARIMA orders, there appears to be no supported, stable way to reapply those orders when models include regressors and are refit on new data.

The attached reprex demonstrates this.

library(tidyverse)
library(fable)
#> Loading required package: fabletools
library(tsibble)
#> 
#> Attaching package: 'tsibble'
#> The following object is masked from 'package:lubridate':
#> 
#>     interval
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, union

set.seed(123)
# 1. Data with exogenous regressor

df <- tsibble(
  date = yearquarter("2000 Q1") + 0:39,
  y = rnorm(40),
  x = factor(rep(c("A", "B"), length.out = 40)),
  index = date
)
# 2. Identification fit (automatic ARIMA)

id_train <- df %>% filter(date <= yearquarter("2008 Q4"))

id_fit <- id_train %>%
  model(auto = ARIMA(y ~ x))

id_fit
#> # A mable: 1 x 1
#>                                    auto
#>                                 <model>
#> 1 <LM w/ ARIMA(0,0,1)(1,0,0)[4] errors>
# 3. Extract ARIMA orders programmatically

orders_tbl <- id_fit %>%
  pivot_longer(everything(), names_to = "model", values_to = "fit") %>%
  as_tibble() %>%
  mutate(
    orders = map_dfr(
      fit,
      \(m) m$fit$spec %>%
        select(p, d, q, P, D, Q, period, constant)
    )
  ) %>%
  select(orders) %>%
  unnest_wider(orders)

orders_tbl
#> # A tibble: 1 × 8
#>       p     d     q     P     D     Q period constant
#>   <int> <int> <int> <int> <int> <int>  <dbl> <lgl>   
#> 1     0     0     1     1     0     0      4 FALSE
# 4. Attempt to reconstruct a fixed-order ARIMA model
#    using pdq() programmatically

fixed_model <- ARIMA(
  y ~ x +
    pdq(
      orders_tbl$p[[1]],
      orders_tbl$d[[1]],
      orders_tbl$q[[1]]
    ) +
    PDQ(
      orders_tbl$P[[1]],
      orders_tbl$D[[1]],
      orders_tbl$Q[[1]],
      period = orders_tbl$period[[1]]
    )
)
# 5. Refit on the SAME data (still works)

df %>%
  model(reused = fixed_model) %>%
  forecast(
    new_data = df %>% slice_tail(n = 4)
  )
#> # A fable: 4 x 5 [1Q]
#> # Key:     .model [1]
#>   .model    date
#>   <chr>    <qtr>
#> 1 reused 2009 Q1
#> 2 reused 2009 Q2
#> 3 reused 2009 Q3
#> 4 reused 2009 Q4
#> # ℹ 3 more variables: y <dist>, .mean <dbl>, x <fct>
# 6. Refit on a DIFFERENT training window (CV semantics)

new_train <- df %>% filter(date <= yearquarter("2006 Q4"))

future_x <- new_train %>%
  new_data(n = 4) %>%
  mutate(
    x = factor("A", levels = levels(df$x))
  )

new_train %>%
  model(reused = fixed_model) %>%
  forecast(new_data = future_x)
#> Warning: It looks like you're trying to fully specify your ARIMA model but have not said
#> if a constant should be included. You can include a constant using `ARIMA(y~1)`
#> to the formula or exclude it by adding `ARIMA(y~0)`.
#> Warning: 1 error encountered for reused
#> [1] Could not find an appropriate ARIMA model.
#> This is likely because automatic selection does not select models with characteristic roots that may be numerically unstable.
#> For more details, refer to https://otexts.com/fpp3/arima-r.html#plotting-the-characteristic-roots
#> # A fable: 4 x 5 [1Q]
#> # Key:     .model [1]
#>   .model    date      y .mean x    
#>   <chr>    <qtr> <dist> <dbl> <fct>
#> 1 reused 2007 Q1     NA    NA A    
#> 2 reused 2007 Q2     NA    NA A    
#> 3 reused 2007 Q3     NA    NA A    
#> 4 reused 2007 Q4     NA    NA A

Created on 2026-03-28 with reprex v2.1.1


Observed behavior

In the reprex:

  1. An automatic ARIMA model with an exogenous regressor is fit successfully.
  2. The ARIMA orders are extracted programmatically from the fitted model.
  3. A new ARIMA model is constructed using pdq() / PDQ() with those extracted orders.
  4. Reusing this model on the same training data works.
  5. Refitting the same stored model on a different training window (CV semantics) fails, producing NA forecasts and ARIMA identification warnings.

This makes fixed‑order reuse unreliable once refitting is involved.


Key issue
  • pdq() / PDQ() work as inline formula syntax, but are not stable for programmatic model reuse under refitting.
  • There is no exported alternative (e.g., a spec object) that allows users to safely freeze and reapply ARIMA orders.
  • As a result, a common workflow—identify ARIMA structure once, then reuse it across windows—is not reliably supported.

Expected behavior

One of the following would resolve the issue:

  • A documented, exported API for constructing a reusable fixed‑order ARIMA specification (with regressors), or
  • Clear documentation stating that pdq() / PDQ() are not supported for model reuse / CV and cannot be relied upon in this way.

Why this matters

Freezing ARIMA error structure across refits is a standard requirement for:

  • expanding‑window cross‑validation
  • fair model comparison
  • production refitting with fixed structure

Currently, these workflows are brittle or unsupported despite order extraction being possible.


Environment
  • R 4.5.3
  • fable (CRAN)
  • fabletools (CRAN)
  • tsibble (CRAN)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the reported behavior with ARIMA(), pdq(), PDQ(), model(), and forecast() using the reprex in the issue. Trace how the model specification is stored and reapplied during refitting, then determine whether a reusable fixed-order API or documentation is appropriate; done means fixed orders with regressors work reliably across training windows, or their limitations are clearly documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
r
Domain
data
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.