Module 9

ARIMA vs ETS

Data + Plot

library(fpp3)       
library(fredr)

fredr_set_key(Sys.getenv("FRED_API_KEY"))

setwd("~/Library/Mobile Documents/com~apple~CloudDocs/Boston College/Spring 2026/Forecasting/Discussions/Module 9")

retail_raw <- fredr(
  series_id = "RSXFSN",
  observation_start = as.Date("2010-01-01"))

retail <- retail_raw |>
  mutate(Month = yearmonth(date)) |>
  select(Month, value) |>
  rename(Sales = value) |>
  as_tsibble(index = Month)

retail |> autoplot(Sales) +
  labs(title = "US Retail Sales",
       y = "Millions of dollars", x = NULL)

Split into Train/Test

train <- retail |> slice(1:floor(0.8 * n()))
test  <- retail |> slice((floor(0.8 * n()) + 1):n())

train
# A tsibble: 155 x 2 [1M]
      Month  Sales
      <mth>  <dbl>
 1 2010 Jan 273665
 2 2010 Feb 269889
 3 2010 Mar 314812
 4 2010 Apr 310622
 5 2010 May 318467
 6 2010 Jun 312635
 7 2010 Jul 314597
 8 2010 Aug 315974
 9 2010 Sep 301406
10 2010 Oct 308709
# ℹ 145 more rows
test
# A tsibble: 39 x 2 [1M]
      Month  Sales
      <mth>  <dbl>
 1 2022 Dec 637438
 2 2023 Jan 535580
 3 2023 Feb 516582
 4 2023 Mar 589560
 5 2023 Apr 574331
 6 2023 May 615939
 7 2023 Jun 597476
 8 2023 Jul 591150
 9 2023 Aug 613511
10 2023 Sep 579306
# ℹ 29 more rows

Fitting both Models

fit <- train |>
  model(
    ets   = ETS(Sales),
    arima = ARIMA(Sales)
  )

fit |> select(ets)   |> report()
Series: Sales 
Model: ETS(M,A,M) 
  Smoothing parameters:
    alpha = 0.5421074 
    beta  = 0.000108813 
    gamma = 0.0001060196 

  Initial states:
     l[0]     b[0]     s[0]    s[-1]     s[-2]     s[-3]    s[-4]    s[-5]
 319974.6 1850.523 1.145351 1.013066 0.9911094 0.9630888 1.031154 1.013457
    s[-6]    s[-7]    s[-8]    s[-9]    s[-10]    s[-11]
 1.013306 1.042289 0.982475 1.016705 0.8888571 0.8991418

  sigma^2:  8e-04

     AIC     AICc      BIC 
3697.537 3702.004 3749.276 
fit |> select(arima) |> report()
Series: Sales 
Model: ARIMA(0,1,2)(0,1,1)[12] 

Coefficients:
          ma1      ma2     sma1
      -0.2226  -0.3221  -0.7293
s.e.   0.0832   0.0845   0.0751

sigma^2 estimated as 164263882:  log likelihood=-1547.8
AIC=3103.61   AICc=3103.9   BIC=3115.43

Interpretation: The ETS selected was ETS(M, A, M) and the ARIMA selected was ARIMA(0, 1, 2)(0, 1, 1)[12]. The models are not equivalent because ETS models with multiplicative components have no exact ARIMA counterpart, and the selected ETS contains multiplicative errors and multiplicative seasonality, while ARIMA is a strictly additive linear framework.

ARIMAX

sent_raw <- fredr(
  series_id = "UMCSENT",
  observation_start = as.Date("2010-01-01")
)

sent <- sent_raw |>
  mutate(Month = yearmonth(date)) |>
  select(Month, value) |>
  rename(Sentiment = value)

#merge sentiment into retail tsibble
retail_x <- retail |>
  left_join(sent, by = "Month")|>
  mutate(Sentiment_lag1 = lag(Sentiment)) #lag sentiment by 1 month

retail_x
# A tsibble: 194 x 4 [1M]
      Month  Sales Sentiment Sentiment_lag1
      <mth>  <dbl>     <dbl>          <dbl>
 1 2010 Jan 273665      74.4           NA  
 2 2010 Feb 269889      73.6           74.4
 3 2010 Mar 314812      73.6           73.6
 4 2010 Apr 310622      72.2           73.6
 5 2010 May 318467      73.6           72.2
 6 2010 Jun 312635      76             73.6
 7 2010 Jul 314597      67.8           76  
 8 2010 Aug 315974      68.9           67.8
 9 2010 Sep 301406      68.2           68.9
10 2010 Oct 308709      67.7           68.2
# ℹ 184 more rows

Split and Fitting the Model

train_x <- retail_x |> slice(1:floor(0.8 * n()))
test_x  <- retail_x |> slice((floor(0.8 * n()) + 1):n())

fit_all <- train_x |>
  model(
    ets    = ETS(Sales),
    arima  = ARIMA(Sales),
    arimax = ARIMA(Sales ~ Sentiment_lag1) #ADDED a lagged term
  )

fit_all |> select(arima) |> report()
Series: Sales 
Model: ARIMA(0,1,2)(0,1,1)[12] 

Coefficients:
          ma1      ma2     sma1
      -0.2226  -0.3221  -0.7293
s.e.   0.0832   0.0845   0.0751

sigma^2 estimated as 164263882:  log likelihood=-1547.8
AIC=3103.61   AICc=3103.9   BIC=3115.43
fit_all |> select(arimax) |> report()
Series: Sales 
Model: LM w/ ARIMA(4,1,0) errors 

Coefficients:
          ar1      ar2      ar3      ar4  Sentiment_lag1  intercept
      -0.6877  -0.7339  -0.4781  -0.3594       -600.7734  1811.8125
s.e.   0.0756   0.0852   0.0847   0.0746        432.0283   750.7964

sigma^2 estimated as 924091793:  log likelihood=-1794.48
AIC=3602.96   AICc=3603.73   BIC=3624.22
fit_all |> select(ets)   |> report()
Series: Sales 
Model: ETS(M,A,M) 
  Smoothing parameters:
    alpha = 0.5421074 
    beta  = 0.000108813 
    gamma = 0.0001060196 

  Initial states:
     l[0]     b[0]     s[0]    s[-1]     s[-2]     s[-3]    s[-4]    s[-5]
 319974.6 1850.523 1.145351 1.013066 0.9911094 0.9630888 1.031154 1.013457
    s[-6]    s[-7]    s[-8]    s[-9]    s[-10]    s[-11]
 1.013306 1.042289 0.982475 1.016705 0.8888571 0.8991418

  sigma^2:  8e-04

     AIC     AICc      BIC 
3697.537 3702.004 3749.276 

Accuracy Table

fc_all <- fit_all |> forecast(new_data = test_x)

accuracy(fc_all, retail_x) |>
  select(.model, RMSE, MAE, MAPE, MASE) |>
  arrange(RMSE)
# A tibble: 3 × 5
  .model   RMSE    MAE  MAPE  MASE
  <chr>   <dbl>  <dbl> <dbl> <dbl>
1 ets    18003. 14353.  2.33 0.613
2 arimax 38812. 28340.  4.80 1.21 
3 arima  38816. 34788.  5.78 1.49 

Conclusion:

I would choose the ETS model because it has the best accuracy metrics. It’s multiplicative error and multiplicative seasonal components capture the proportional holiday spikes in retail sales better than the ARIMA models. The ARIMAX outperformed the plain ARIMA on every metric, confirming the expectation that a good external regressor should improve the performance of a basic ARIMA model. Lagged consumer sentiment added forecasting power on top of the pure time-series structure, but not enough to beat the ETS model.

Plot

zoom_start <- min(test_x$Month) - 12   # show 12 months of training before test starts

fc_all |>
  autoplot(train_x, level = 95) +
  autolayer(test_x, Sales, colour = "black", linewidth = 0.5) +
  coord_cartesian(xlim = c(zoom_start, max(test_x$Month))) +
  labs(title = "ETS vs ARIMA vs ARIMAX Forecasts: Retail Sales",
       subtitle = "Test period with 12 months of prior training context",
       y = "Millions of dollars", x = NULL) +
  theme_minimal()