213. STL

213.1. STL Decomposition (Seasonal-Trend using Loess)

Modern alternative to classical decomposition. Robust, handles changing seasonal patterns, and produces complete trend/seasonal estimates with no endpoint gaps.

Developed by Cleveland, Cleveland, McRae, and Terpenning (1990). Standard tool in statsmodels, R::stats, forecast::stl.

213.1.1. What it does

Decompose a series additively:

where is trend, is seasonal, is the remainder (noise). Multiplicative decomposition is handled by first taking , decomposing additively, then exponentiating.

Same components as classical decomposition, but the estimation method is different:

213.1.2. Key parameters

Parameter Typical value Effect
np (period) seasonal period (e.g., 12 for monthly, 7 for daily-with-weekly-seasonality) Defines the seasonal cycle
ns (seasonal window) 7-15 (must be odd ≥ 7) Smaller → seasonal pattern can change rapidly. Larger → seasonal pattern is locked-in over time. Set based on how stable seasonality is.
nt (trend window) auto: Larger → smoother trend. Smaller → trend tracks short-term moves.
nl (low-pass window) next odd integer Internal; usually leave default.
robust True / False If True, downweights outliers via a second pass. Use when there are known anomalies.

213.1.3. Why STL beats classical decomposition

Issue Classical STL
Seasonal pattern Constant across cycles Can evolve gradually (controlled by ns)
Endpoints Gaps of at start and end Smooth all the way through
Outliers Sensitive — one anomaly distorts seasonal index Robust mode downweights anomalies
Multiplicative decomp Separate procedure Take logs, decompose additively, exponentiate

213.1.4. Workflow

  1. Plot the series, look at seasonality and trend.
  2. Choose np (= seasonal period).
  3. Try default STL — usually good. Inspect residuals: should be structureless.
  4. If seasonal pattern is clearly shifting, lower ns. If residuals show seasonal structure (under-fit), lower ns.
  5. If outliers, enable robust mode.
  6. Use the components for: forecasting (forecast each component, recombine), anomaly detection (flag large residuals), seasonal-adjustment (subtract seasonal for trend-only view).

213.1.5. When STL doesn’t work

Example

Given: 5 years of monthly retail sales with growing seasonal swing.

Setup:

  • (monthly data with yearly seasonality)
  • (seasonal pattern can evolve slowly)
  • Robust mode on (Black-Friday-style anomalies expected)

Output: three series, each of length 60.

  • Trend : smooth upward curve, captures the multi-year growth.
  • Seasonal : 12-month repeating pattern, but the December peak grows from year 1 (+10%) to year 5 (+18%) — that’s the evolving seasonality STL captures.
  • Remainder : small, mean-zero, ideally autocorrelation-free.

Use the output:

  • Forecast: extend trend (e.g., linear extrapolation or random-walk) + use the most-recent seasonal pattern.
  • Seasonally-adjusted series: . Useful for tracking underlying performance ignoring seasonal effects (Black Friday, summer, etc.).
  • Anomaly detection: large residuals flag anomalous months.

Code (Python):

from statsmodels.tsa.seasonal import STL
result = STL(y, period=12, seasonal=13, robust=True).fit()
trend = result.trend
seasonal = result.seasonal
remainder = result.resid

Plot all four (result.plot()) for visual diagnosis.