Module 5 Discussion

Author

Kevin Rusu

Explain Smoothing Operators in ETS Model

ETS stands for Error, Trend, and Seasonality. It is an exponential smoothing model that decomposes a time series into three components and model each one. The letters represent the type of each component:

  • Error (E): Additive (A) or Multiplicative (M)

  • Trend (T): None (N), Additive (A), Additive Damped (Ad), or Multiplicative (M)

  • Seasonality (S): None (N), Additive (A), or Multiplicative (M)

Smoothing parameters control how much weight each recent vs. older observation is given. The parameters are between 0 and 1. A value close 1 means recent data is weighted more heavily and past data is weighted less. In contrast, a value close to 0 means older values have higher weight, and recent ones have less weight.

Alpha (Level Smoothing)

Alpha (\(\alpha\)) controls how quickly the baseline value of the series adapts to new data. A value close to 1 means the model reacts fast to every new data point, while a value close to 0 means the model is very stable and slow to update.

Beta (Trend Smoothing)

Beta (\(\beta\)) controls how quickly the estimated trend (the slope) updates over time. It only exists when there is a trend component (T = A or Ad). A high \(\beta\) means that the slope adjust quickly, so the model sees trend changes fast. A low \(\beta\) means that the slope changes slowly, which is useful when we believe the trend is stable and will not flip around.

Gamma (Seasonal Smoothing)

Gamma (\(\gamma\)) controls how quickly the seasonal factors update. It only exists when there is a seasonal component (S = A or M). A high \(\gamma\) means that seasonal patterns adapt quickly from year to year, and a low \(\gamma\) means that seasonal patterns are assumed to be very stable across the years.

Phi (Damping Parameter)

Phi (\(\phi\)) is the damping parameter for the trend. It only exists when you have a damped trend (T = Ad). Its range is typically constrained between 0.8 and 0.98. It tells us how quickly the trend “fades out” for our forecast in the future. Without damping, a trend projects forever at the same slope, which is often unrealistic.

Visualize 2 Time Series

library(fpp3)
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.4.1
✔ ggplot2     4.0.0     
── 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)
library(patchwork)
library(scales)

fredr_set_key(Sys.getenv("FRED_API_KEY"))
retail_raw <- fredr(
  series_id = "RSXFS",
  observation_start = as.Date("2010-01-01")
)

indpro_raw <- fredr(
  series_id = "INDPRO",
  observation_start = as.Date("2010-01-01")
)
#retail sales to tsibble
retail_ts <- retail_raw |>
  mutate(Month = yearmonth(date)) |>
  as_tsibble(index = Month) |>
  select(Month, value) |>
  rename(retail_sales = value)

#Industrial Prod to tsibble
indpro_ts <- indpro_raw |>
  mutate(Month = yearmonth(date)) |>
  as_tsibble(index = Month) |>
  select(Month, value) |>
  rename(indpro = value)
head(retail_ts)
# A tsibble: 6 x 2 [1M]
     Month retail_sales
     <mth>        <dbl>
1 2010 Jan       302325
2 2010 Feb       302310
3 2010 Mar       309525
4 2010 Apr       312143
5 2010 May       309158
6 2010 Jun       308588
head(indpro_ts)
# A tsibble: 6 x 2 [1M]
     Month indpro
     <mth>  <dbl>
1 2010 Jan   89.3
2 2010 Feb   89.7
3 2010 Mar   90.3
4 2010 Apr   90.6
5 2010 May   91.8
6 2010 Jun   92.0
tail(retail_ts)
# A tsibble: 6 x 2 [1M]
     Month retail_sales
     <mth>        <dbl>
1 2025 Jul       628747
2 2025 Aug       632149
3 2025 Sep       632395
4 2025 Oct       631346
5 2025 Nov       634711
6 2025 Dec       634738
tail(indpro_ts)
# A tsibble: 6 x 2 [1M]
     Month indpro
     <mth>  <dbl>
1 2025 Aug   102.
2 2025 Sep   102.
3 2025 Oct   101.
4 2025 Nov   101.
5 2025 Dec   102.
6 2026 Jan   102.
#plots
p1 <- autoplot(retail_ts, retail_sales) +
  scale_y_continuous(labels = label_comma()) +
  labs(
    title = "U.S. Retail & Food Services Sales",
    subtitle = "Jan 2010 - Present (Monthly)",
    y = "Millions of USD",
    x = NULL
  ) +
  theme_minimal()

p2 <- autoplot(indpro_ts, indpro) +
  labs(
    title = "U.S. Industrial Production Index",
    subtitle = "Jan 2010 - Present (Monthly)",
    y = "Index (2017 = 100)",
    x = NULL
  ) +
  theme_minimal()

p1/p2

ggsave(
  filename = "ets_series_plots.png",
  plot = p1 / p2,
  width = 8,
  height = 6,
  dpi = 600,          
  device = "png"
)

US Retail & Food Service Sales

The retail sales series shows a consistent upward trend from 2010 onwards, with only one major disruption from the COVID-19 shock in early 2020. The level recovers quickly and continues climbing.

Based on this, I expect alpha to be moderate (around 0.3 to 0.5), since the level evolves predictably. I expect Beta to be close to zero because the trend is remarkably stable over 15 years and rarely changes direction. I don’t think there will be a value for Gamma because from eyeballing it, it does not seem that there is sessonality. However, I know that retail sales are higher before christmas time, so there could be a value for Gamma, which could be around 0.1. I do not expect a value for Phi because it seems that the time series has no damping.

U.S. Industrial Production Index

The industrial production series also trends upward from 2010, but the pattern is less clean. The trend stalls around 2015, and the series is visibly more volatile with greater short-term noise compared to retail sales. The COVID crash in 2020 is sharp and severe, but recovery is fast.

I expect alpha to be around 0.5 to 0.8, as the model needs to adapt the level estimate more aggressively to track sudden changes. I do not see a strong trend, so I expect no Beta. There might be a value for gamma, but I don’t think the seasonality is strong enough.

Find best ETS Model

Split into Train/Test

retail_train <- retail_ts |>
  filter_index(. ~ "2023 Dec")

indpro_train <- indpro_ts |>
  filter_index(. ~ "2024 Jan")

Fit ETS

retail_fit <- retail_train |>
  model(ETS = ETS(retail_sales))

indpro_fit <- indpro_train |>
  model(ETS = ETS(indpro))

report(retail_fit)
Series: retail_sales 
Model: ETS(M,A,N) 
  Smoothing parameters:
    alpha = 0.6697469 
    beta  = 0.0001000428 

  Initial states:
     l[0]     b[0]
 302366.8 1732.987

  sigma^2:  4e-04

     AIC     AICc      BIC 
3916.392 3916.762 3932.012 
report(indpro_fit)
Series: indpro 
Model: ETS(A,N,N) 
  Smoothing parameters:
    alpha = 0.9999 

  Initial states:
     l[0]
 89.29008

  sigma^2:  1.7238

     AIC     AICc      BIC 
962.9677 963.1132 972.3574 

Plot Forecast

retail_fc <- retail_fit |> forecast(h = 24)
indpro_fc <- indpro_fit |> forecast(h = 24)

p3 <- retail_fc |>
  autoplot(retail_ts) +
  labs(
    title = "ETS Forecast - U.S. Retail Sales",
    y = "Millions of USD",
    x = NULL
  ) +
  scale_y_continuous(labels = label_comma()) +
  theme_minimal()+
  theme(legend.position = "bottom")

p4 <- indpro_fc |>
  autoplot(indpro_ts) +
  labs(
    title = "ETS Forecast - U.S. Industrial Production",
    y = "Index (2017 = 100)",
    x = NULL
  ) +
  theme_minimal()+
  theme(legend.position = "bottom")

p3

p4

ggsave(
  filename = "ets_series_plots.png",
  plot = p3,
  width = 8,
  height = 6,
  dpi = 300,          # 300 DPI is standard for academic work
  device = "png"
)

ggsave(
  filename = "ets_series_plots2.png",
  plot = p4,
  width = 8,
  height = 6,
  dpi = 300,          # 300 DPI is standard for academic work
  device = "png"
)

U.S. Retail & Food Services Sales - ETS(M, A, N)

R selected an ETS(M, A, N) model for retail sales, which stands for multiplicative errors, an additive trend, and no seasonality. The multiplicative error makes sense here because the series grows steadily over time, meaning the size of random fluctuations tends to scale with the level of the series rather than staying constant. The additive trend captures the consistent upward growth visible in the plot. Seasonality was excluded.

  • Alpha = 0.67: Higher than my expected range of 0.3 to 0.5. I believe the Covid shock forced the model to weight recent observations more heavily to adapt quickly to sudden change

  • Beta = 0.0001: Confirmed my intuition. The trend is remarkably stable over 15 years and barely changes direction, so the slope almost never needs updating.

  • Gamma = NA: Seasonal Patterns seem to be not strong enough for model

  • Phi = NA: There is no damping applied to the model

U.S. Industrial Production Index - ETS(A, N, N)

R selected an ETS(A, N, N) model for industrial production, meaning additive errors, no trend, and no seasonality. This is the simplest possible ETS specification, equivalent to simple exponential smoothing. The additive error structure is appropriate because the series does not grow dramatically in scale over the sample period. Both trend and seasonality were excluded.

  • Alpha = 0.9999: Alpha near 1 means the model almost entirely ignores past observations and uses only the most recent value, which reflects the high volatility and structural shifts in the series.

  • Beta = NA: Model detected no trend that was important for forecast.

  • Gamma = NA: No seasonality

  • Phi = NA: No trend, so there is also no damping