Show Code
library(fpp3)
library(tidyverse)
library(fredr)
library(kableExtra)
library(patchwork)
fredr_set_key("cdbfb0bbfdb75085e9eea36ef1806968")The McGuigan textbook (Chapter 5, Equation 5.15) introduces first-order exponential smoothing as:
\[\hat{Y}_{t+1} = wY_t + (1 - w)\hat{Y}_t\]
where \(w\) is a weight between 0 and 1 placed on the most recent observation, and \((1 - w)\) is the weight placed on the previous forecast. A large \(w\) means the model reacts quickly to recent data; a small \(w\) produces smoother, slower-moving forecasts.
The ETS (Error, Trend, Seasonality) framework generalizes this idea into a complete family of exponential smoothing models. Every ETS model has up to four smoothing parameters, each controlling how quickly the model adapts to a different component of the series.
Alpha (\(\alpha\)) — Level Smoothing
Alpha is the direct extension of McGuigan’s \(w\). It controls how quickly the estimated level of the series updates in response to new observations.
\[\ell_t = \alpha y_t + (1 - \alpha)\ell_{t-1}\]
Beta (\(\beta\)) — Trend Smoothing
Beta controls how quickly the estimated trend (slope) updates over time. It only appears in models with a trend component (ETS models where the middle letter is A for additive trend or M for multiplicative trend).
\[b_t = \beta(\ell_t - \ell_{t-1}) + (1 - \beta)b_{t-1}\]
Gamma (\(\gamma\)) — Seasonal Smoothing
Gamma controls how quickly the estimated seasonal indices update. It only appears in models with a seasonal component (ETS models where the last letter is A or M).
\[s_t = \gamma(y_t - \ell_{t-1} - b_{t-1}) + (1 - \gamma)s_{t-m}\]
where \(m\) is the seasonal period (12 for monthly data, 4 for quarterly).
Phi (\(\phi\)) — Damping Parameter
Phi is unique — it does not smooth a component but instead controls whether a trend is allowed to persist indefinitely or is gradually dampened toward zero.
| Parameter | Component | Range | Low value means | High value means |
|---|---|---|---|---|
| \(\alpha\) | Level | (0,1) | Long memory, slow adaptation | Short memory, fast adaptation |
| \(\beta\) | Trend | (0,1) | Stable slope | Volatile slope |
| \(\gamma\) | Seasonality | (0,1) | Fixed seasonal pattern | Evolving seasonal pattern |
| \(\phi\) | Damping | (0,1) | Trend dies quickly | Trend persists longer |
McGuigan’s \(w\) in Equation 5.15 is exactly \(\alpha\) in ETS(A,N,N). His observation that “a large \(w\) indicates that a heavy weight is being placed on the most recent observation” is the intuition behind alpha across all ETS variants. The ETS framework simply extends this one parameter into a full system where the level, trend, and seasonal components each have their own adaptive smoothing rate.
The key insight from Equation 5.18 in the textbook is that exponential smoothing is a weighted average of all past observations with geometrically declining weights — the most recent observation gets weight \(w\), the one before gets \(w(1-w)\), the one before that gets \(w(1-w)^2\), and so on. This geometric decay is what distinguishes exponential smoothing from simple moving averages which give equal weight to the last \(N\) observations and zero weight to everything before.
library(fpp3)
library(tidyverse)
library(fredr)
library(kableExtra)
library(patchwork)
fredr_set_key("cdbfb0bbfdb75085e9eea36ef1806968")retail_raw <- fredr(
series_id = "RSAFS",
observation_start = as.Date("2010-01-01"),
observation_end = as.Date("2023-12-01")
)
retail_ts <- retail_raw |>
select(date, value) |>
mutate(Month = yearmonth(date)) |>
as_tsibble(index = Month) |>
rename(Sales = value) |>
select(Month, Sales)p1 <- retail_ts |>
autoplot(Sales) +
labs(
title = "US Advance Retail Sales (RSAFS)",
subtitle = "Monthly | Jan 2010 to Dec 2023",
y = "Millions of Dollars",
x = NULL
) +
theme_minimal()
p2 <- retail_ts |>
gg_season(Sales, labels = "both") +
labs(title = "Seasonal Plot", y = NULL, x = NULL) +
theme_minimal()
p1 / p2Eyeballed parameter expectations for Retail Sales:
Looking at the plot, retail sales shows a strong upward trend from 2010 to 2023 with a sharp COVID dip in early 2020 followed by an unusually rapid recovery. The seasonal pattern is consistent every year with a December peak and a February trough. Based on this visual inspection:
unemp_raw <- fredr(
series_id = "UNRATE",
observation_start = as.Date("2010-01-01"),
observation_end = as.Date("2023-12-01")
)
unemp_ts <- unemp_raw |>
select(date, value) |>
mutate(Month = yearmonth(date)) |>
as_tsibble(index = Month) |>
rename(Unemployment = value) |>
select(Month, Unemployment)p3 <- unemp_ts |>
autoplot(Unemployment) +
labs(
title = "US Unemployment Rate (UNRATE)",
subtitle = "Monthly | Jan 2010 to Dec 2023",
y = "Percent",
x = NULL
) +
theme_minimal()
p4 <- unemp_ts |>
gg_subseries(Unemployment) +
labs(title = "Subseries Plot by Month", y = NULL, x = NULL) +
theme_minimal()
p3 / p4Eyeballed parameter expectations for Unemployment:
The unemployment rate starts around 9.8% in early 2010, declines steadily to about 3.5% by 2019, spikes dramatically to 14.7% in April 2020 due to COVID, then recovers rapidly back to around 3.5% by late 2022. The subseries plot shows very little consistent seasonal pattern — unemployment is not strongly seasonal in the US.
# Fit best ETS model to both series using AIC selection
fit_retail <- retail_ts |>
model(ETS = ETS(Sales))
fit_unemp <- unemp_ts |>
model(ETS = ETS(Unemployment))
# Extract model reports
report_retail <- fit_retail |> report()Series: Sales
Model: ETS(M,A,N)
Smoothing parameters:
alpha = 0.7109986
beta = 0.0001000185
Initial states:
l[0] b[0]
339200.4 1887.068
sigma^2: 6e-04
AIC AICc BIC
3996.143 3996.513 4011.763
report_unemp <- fit_unemp |> report()Series: Unemployment
Model: ETS(M,A,N)
Smoothing parameters:
alpha = 0.9999
beta = 0.9999
Initial states:
l[0] b[0]
9.517881 -0.6905423
sigma^2: 0.0238
AIC AICc BIC
811.6534 812.0238 827.2733
report_retail| ETS |
|---|
| <ETS(M,A,N)> |
# Extract smoothing parameters
retail_params <- fit_retail |>
tidy() |>
filter(term %in% c("alpha", "beta", "gamma", "phi")) |>
select(term, estimate) |>
rename(Parameter = term, Estimate = estimate)
retail_params |>
kbl(
digits = 4,
caption = "ETS Smoothing Parameters — US Retail Sales"
) |>
kable_styling(
bootstrap_options = c("striped", "hover"),
full_width = FALSE
)| Parameter | Estimate |
|---|---|
| alpha | 0.7110 |
| beta | 0.0001 |
fit_retail |>
forecast(h = 24) |>
autoplot(retail_ts, level = c(80, 95)) +
labs(
title = "ETS Forecast: US Retail Sales",
subtitle = paste("Model:", fit_retail |> select(ETS) |> as.character()),
y = "Millions of Dollars",
x = NULL
) +
theme_minimal()Reconciling Retail Sales Results:
Insert your actual parameter values from the model output above and compare to your predictions. Example reconciliation:
The best-selected ETS model for retail sales is typically ETS(M,Ad,M) or ETS(A,Ad,A) reflecting the multiplicative seasonal pattern growing with the level and a damped trend. Comparing to my predictions:
report_unemp| ETS |
|---|
| <ETS(M,A,N)> |
unemp_params <- fit_unemp |>
tidy() |>
filter(term %in% c("alpha", "beta", "gamma", "phi")) |>
select(term, estimate) |>
rename(Parameter = term, Estimate = estimate)
unemp_params |>
kbl(
digits = 4,
caption = "ETS Smoothing Parameters — US Unemployment Rate"
) |>
kable_styling(
bootstrap_options = c("striped", "hover"),
full_width = FALSE
)| Parameter | Estimate |
|---|---|
| alpha | 0.9999 |
| beta | 0.9999 |
fit_unemp |>
forecast(h = 24) |>
autoplot(unemp_ts, level = c(80, 95)) +
labs(
title = "ETS Forecast: US Unemployment Rate",
subtitle = paste("Model:", fit_unemp |> select(ETS) |> as.character()),
y = "Percent",
x = NULL
) +
theme_minimal()Reconciling Unemployment Results:
The best ETS model for unemployment is likely to be ETS(A,Ad,N) or ETS(A,N,N) reflecting the absence of meaningful seasonality and the presence of a declining trend that needs damping.
ETS(A,N,N) is simple exponential smoothing — exactly McGuigan’s first-order model with additive errors. The forecast equation is:
\[\hat{y}_{t+1|t} = \ell_t = \alpha y_t + (1-\alpha)\ell_{t-1}\]
The model has a single parameter: \(\alpha\). We find it by minimizing the sum of squared errors (SSE) across the training period.
# Fit ETS(ANN) explicitly — forces simple exponential smoothing
fit_ann <- retail_ts |>
model(ETS_ANN = ETS(Sales ~ error("A") + trend("N") + season("N")))
report_ann <- fit_ann |> report()Series: Sales
Model: ETS(A,N,N)
Smoothing parameters:
alpha = 0.9998986
Initial states:
l[0]
347406.6
sigma^2: 132474569
AIC AICc BIC
4006.733 4006.880 4016.105
report_ann| ETS_ANN |
|---|
| <ETS(A,N,N)> |
alpha_r <- fit_ann |>
tidy() |>
filter(term == "alpha") |>
pull(estimate)
cat("Alpha estimated by R (ETS ANN):", round(alpha_r, 6), "\n")Alpha estimated by R (ETS ANN): 0.999899
fit_ann |>
augment() |>
ggplot(aes(x = Month)) +
geom_line(aes(y = Sales, colour = "Actual")) +
geom_line(aes(y = .fitted, colour = "Fitted"), linetype = "dashed") +
scale_colour_manual(values = c("Actual" = "#2c3e50", "Fitted" = "#e74c3c")) +
labs(
title = "ETS(ANN) Fitted Values vs Actual",
subtitle = paste0("Alpha = ", round(alpha_r, 4)),
y = "Sales (Millions of Dollars)",
x = NULL,
colour = NULL
) +
theme_minimal() +
theme(legend.position = "bottom")To replicate the alpha estimate in Excel, set up the spreadsheet as follows:
Column structure:
| Column | Content | Formula |
|---|---|---|
| A | Month (1, 2, 3, …) | |
| B | Actual Sales \(Y_t\) | Data values |
| C | Level \(\ell_t\) | C2 = B2 (initialize to first observation) |
C3 = =$B$1*B2 + (1-$B$1)*C2 (drag down) |
||
| D | Forecast \(\hat{Y}_t\) | D3 = C2 (one-step-ahead forecast = lagged level) |
| E | Error \(e_t = Y_t - \hat{Y}_t\) | E3 = =B3-D3 |
| F | Squared Error \(e_t^2\) | F3 = =E3^2 |
| B1 | Alpha parameter | Start with any value between 0 and 1 |
| SSE | Sum of squared errors | =SUM(F3:F168) |
Solver settings:
The alpha value Solver produces should match the R estimate to at least 3 decimal places. Any small difference reflects rounding in initialization or convergence tolerance rather than a methodological error.
(Attach Excel screenshot here showing Solver dialog and matching alpha value)
# Compute SSE for reference to verify Excel calculation
sse_r <- fit_ann |>
augment() |>
summarise(SSE = sum(.resid^2, na.rm = TRUE)) |>
pull(SSE)
cat("SSE from R ETS(ANN):", round(sse_r, 2), "\n")SSE from R ETS(ANN): 69116767 236349 54671965 8385365 9064354 117172.8 1201140 2484126 8947037 16291743 10467872 6397500 5833463 9494070 10892061 4183393 99094.44 9498527 20934.51 174711.7 17489478 4689061 1476758 317107.7 12574521 22842277 1927889 2640168 486038.6 12076116 3184968 23407994 9969746 34849.41 4901712 1918847 11163219 21943029 8378276 3969233 5981928 1548153 5509001 332654.5 124567.7 1836122 1350563 5067531 10567517 26777215 20200750 18631789 500467.8 1106855 599241.1 10890518 1415304 2937383 1464520 9017272 3618762 1186341 42613343 678418.5 11526592 7455.31 15928151 8573.92 3928361 1378748 2591717 3408318 7197485 13327815 1589188 5749791 1528297 22535199 200273.1 24634.76 9672199 626179.8 2032.23 29300618 26087053 16251.74 372115.8 2566206 6359665 5644161 1661.31 866753.3 94169447 147443.4 23445341 6956358 4716423 6847537 6282.98 1936.71 60933706 2997084 8490373 91988.06 3755725 24096352 5946270 94882266 1758310 1719073 56868685 2679.55 29138461 260658.4 14745997 12043601 10749534 1865048 12724477 2726995 557759 44489.06 2209562026 4529392898 5992955029 1567910262 68393123 19641190 111670983 4782658 26762222 21219618 427764437 205546806 3444345529 29008442 13531024 44457252 102663993 13801024 17126160 79253072 39713973 24357791 133876465 43469927 167098753 84057913 119764.9 39262316 43064633 16122914 2929551 46469124 84866638 59227396 811635653 65579424 50152186 29401144 11618210 10338447 1709101 40083239 21609870 10591958 672941.1 900443.2
cat("Alpha from R ETS(ANN):", round(alpha_r, 6), "\n")Alpha from R ETS(ANN): 0.999899
cat("These values should match your Excel Solver output.\n")These values should match your Excel Solver output.
| Retail Sales | Unemployment | |
|---|---|---|
| Best ETS model | insert from output | insert from output |
| Alpha (predicted) | 0.3 to 0.5 | 0.6 to 0.9 |
| Alpha (actual) | insert | insert |
| Beta (predicted) | 0.05 to 0.15 | 0.01 to 0.10 |
| Beta (actual) | insert | insert |
| Gamma (predicted) | 0.05 to 0.15 | Near zero or absent |
| Gamma (actual) | insert | insert |
| Intuition correct? | assess | assess |
The key insight connecting McGuigan’s Chapter 5 to ETS theory is that all exponential smoothing models are governed by the same fundamental tradeoff: a high smoothing parameter produces a reactive model that tracks recent data closely but is susceptible to noise; a low smoothing parameter produces a stable model that resists short-term fluctuations but may be slow to detect genuine structural changes. The ETS framework simply applies this tradeoff separately and independently to the level, trend, and seasonal components of a series.