ETS Model

Author

Ethan Wright

Part 1: Smoothing Parameters

The smoothing parameters in an ETS model are \(\alpha\), \(\beta\), \(\gamma\), and \(\phi\). Error can be A or M, Trend can be A, Ad, M, or N (none), and Seasonality can be A, M, or N

\(\alpha\) is the level parameter that controls how the estimated level adjusts after each observation. The closer \(\alpha\) is to 1, the more weight is given to recent data points.

\(\beta\) is the trend parameter that controls how the slope adjusts after each observation. The higher \(\beta\) is, the faster the trend grows.

\(\gamma\) is the seasonal parameter that controls the updating of the seasonal factors. As y grows, the seasonality changes rapidly.

\(\phi\) is the dampening parameter (Ad in trend). This controls the trend so that it doesn’t reach infinity. As \(\phi\) gets smaller, the dampening increases, i.e the trend flattens.

Part 2:

library(fpp3)
Warning: package 'fpp3' was built under R version 4.5.2
Registered S3 method overwritten by 'tsibble':
  method               from 
  as_tibble.grouped_df dplyr
── Attaching packages ──────────────────────────────────────────── fpp3 1.0.2 ──
✔ tibble      3.3.0     ✔ tsibble     1.1.6
✔ dplyr       1.1.4     ✔ tsibbledata 0.4.1
✔ tidyr       1.3.1     ✔ feasts      0.4.2
✔ lubridate   1.9.4     ✔ fable       0.5.0
✔ ggplot2     3.5.2     
Warning: package 'tsibble' was built under R version 4.5.2
Warning: package 'tsibbledata' was built under R version 4.5.2
Warning: package 'feasts' was built under R version 4.5.2
Warning: package 'fabletools' was built under R version 4.5.2
Warning: package 'fable' was built under R version 4.5.2
── Conflicts ───────────────────────────────────────────────── fpp3_conflicts ──
✖ lubridate::date()    masks base::date()
✖ dplyr::filter()      masks stats::filter()
✖ tsibble::intersect() masks base::intersect()
✖ tsibble::interval()  masks lubridate::interval()
✖ dplyr::lag()         masks stats::lag()
✖ tsibble::setdiff()   masks base::setdiff()
✖ tsibble::union()     masks base::union()
library(fredr)
Warning: package 'fredr' was built under R version 4.5.2
library(tidyverse)
Warning: package 'stringr' was built under R version 4.5.2
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ forcats 1.0.1     ✔ readr   2.1.5
✔ purrr   1.1.0     ✔ stringr 1.6.0
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter()     masks stats::filter()
✖ tsibble::interval() masks lubridate::interval()
✖ dplyr::lag()        masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(patchwork)
library(feasts)
library(fabletools)
library(quantmod)
Loading required package: xts
Loading required package: zoo

Attaching package: 'zoo'

The following object is masked from 'package:tsibble':

    index

The following objects are masked from 'package:base':

    as.Date, as.Date.numeric


######################### Warning from 'xts' package ##########################
#                                                                             #
# The dplyr lag() function breaks how base R's lag() function is supposed to  #
# work, which breaks lag(my_xts). Calls to lag(my_xts) that you type or       #
# source() into this session won't work correctly.                            #
#                                                                             #
# Use stats::lag() to make sure you're not using dplyr::lag(), or you can add #
# conflictRules('dplyr', exclude = 'lag') to your .Rprofile to stop           #
# dplyr from breaking base R's lag() function.                                #
#                                                                             #
# Code in packages is not affected. It's protected by R's namespace mechanism #
# Set `options(xts.warn_dplyr_breaks_lag = FALSE)` to suppress this warning.  #
#                                                                             #
###############################################################################

Attaching package: 'xts'

The following objects are masked from 'package:dplyr':

    first, last

Loading required package: TTR
Registered S3 method overwritten by 'quantmod':
  method            from
  as.zoo.data.frame zoo 
library(tseries)
remove(list=ls())

fredr_set_key("18f0dfa970a5782481171e8fa3980e41")

cpi <- fredr(series_id = "CPIAUCNS",
              observation_start = as.Date("2015-01-01"),
              observation_end   = as.Date("2025-01-01")
              )|>
  transmute(Month = yearmonth(date), value) |>
  as_tsibble(index = Month)

autoplot(cpi) +
  labs(
    title    = "U.S. CPI-U (Not Seasonally Adjusted)",
    subtitle = "Jan 2015 – Jan 2025",
    x        = NULL,
    y        = "CPI Index"
    )
Plot variable not specified, automatically selected `.vars = value`

retail<- fredr(
  series_id         = "MRTSSM44X72USS",
  observation_start = as.Date("2015-01-01"),
  observation_end   = as.Date("2025-01-01"),
  frequency         = "m"
)|>
  transmute(Month = yearmonth(date), value) |>
  as_tsibble(index = Month)

autoplot(retail) +
  labs(
    title    = "U.S. Retail & Food Services Sales (Not Seasonally Adjusted)",
    subtitle = "Jan 2015 – Dec 2025",
    x        = NULL,
    y        = "Sales ($ Millions)"
  )
Plot variable not specified, automatically selected `.vars = value`

Looking at CPI, there is a steady upward trend with low seasonality and no dampening. \(\alpha=0.8\), \(\beta=0.1\), \(\gamma=.05\). Not much dampening, so \(\phi\) should be close to 1

For retail sales, there is far stronger seasonality, but a slightly more varying level. \(\alpha=0.8\), \(\beta=0.1\), \(\gamma=.25\). Not much dampening, so \(\phi\) should be close to 1

Part 3:

ets_cpi<- cpi|>model(ETS(value))
ets_retail<- retail|>model(ETS(value))
report(ets_cpi)
Series: value 
Model: ETS(M,Ad,A) 
  Smoothing parameters:
    alpha = 0.9982612 
    beta  = 0.1303591 
    gamma = 0.001718053 
    phi   = 0.979875 

  Initial states:
     l[0]      b[0]      s[0]      s[-1]      s[-2]     s[-3]     s[-4]
 234.4854 0.3197552 -1.667393 -0.7492027 0.07192148 0.2358367 0.5529372
     s[-5]    s[-6]     s[-7]     s[-8]      s[-9]    s[-10]    s[-11]
 0.8292733 1.110623 0.7660551 0.4617944 0.07397405 -0.555484 -1.130335

  sigma^2:  0

     AIC     AICc      BIC 
518.3716 525.0775 568.6958 
report(ets_retail)
Series: value 
Model: ETS(A,N,N) 
  Smoothing parameters:
    alpha = 0.9998902 

  Initial states:
   l[0]
 436159

  sigma^2:  183284882

     AIC     AICc      BIC 
2886.487 2886.692 2894.874 

For CPI, \(\alpha\) is almost 1. I wouldn’t say it’s surprising since inflation prices are always based closely on the most recent price, but being that high means it’s basically ignoring historical prices> prices are basically just based on the last observation. I was very close to \(\beta\) since the trend doesn’t vary much. \(\gamma\) was also not far away, but I would’ve guessed slightly more seasonality than .001. As expected, \(\phi\) was close to 1.

For Retail sales, \(\alpha\) is basically 1. I wouldn’t say it’s surprising for the same reason as inflation (both are price-based). The big shock came in that there is no seasonality in the data. I would’ve expected some since spending increases in Q4 and dampens in the early months of the year.

Part 4:

retail_ann<- retail|> model(ETS(value~error("A")+trend("N")+season("N")))
report(retail_ann)
Series: value 
Model: ETS(A,N,N) 
  Smoothing parameters:
    alpha = 0.9998902 

  Initial states:
   l[0]
 436159

  sigma^2:  183284882

     AIC     AICc      BIC 
2886.487 2886.692 2894.874 

Interestingly, when I plug the initial state that R gave me into Excel, it gives me alpha of 0.697 rather than 0.998. I ran this multiple times with different starting states and alphas. It always converged to around 0.7. I checked this with manual plugging of alpha, and it showed that 0.7 was the minimum SSE.