Seasonal Decomposition in Python — Core Concepts

Why decomposition is the first step

Before forecasting, anomaly detection, or feature engineering, you need to understand what drives your time series. Decomposition reveals the structure hiding inside the raw data, answering three questions at once: where is the series heading, what repeats, and what is random?

The two decomposition models

Additive decomposition

y(t) = Trend(t) + Seasonal(t) + Residual(t)

Use this when seasonal fluctuations stay roughly the same size over time. If January always adds about 200 extra sales regardless of whether total sales are 1,000 or 10,000, the seasonality is additive.

Multiplicative decomposition

y(t) = Trend(t) × Seasonal(t) × Residual(t)

Use this when seasonal fluctuations scale with the level. If January is always about 15% above average, the absolute swing grows as the business grows. Most business data with growth trends follows this pattern.

Quick test: plot the series. If the amplitude of seasonal swings grows with the level, go multiplicative. If swings stay constant, go additive.

Classical decomposition in Python

from statsmodels.tsa.seasonal import seasonal_decompose
import matplotlib.pyplot as plt

result = seasonal_decompose(
    series,
    model="additive",  # or "multiplicative"
    period=12,          # 12 for monthly data with yearly cycles
)

fig = result.plot()
plt.tight_layout()

This produces four panels: observed data, trend, seasonal pattern, and residuals. The trend is extracted using a centered moving average, the seasonal component is the average of detrended values for each period position, and the residual is whatever is left.

Limitations of classical decomposition

  • The trend estimate is unavailable for the first and last period/2 observations.
  • The seasonal component is assumed to be exactly the same every cycle — it cannot evolve over time.
  • It is sensitive to outliers because it uses simple averages.

STL decomposition: the modern approach

STL (Seasonal and Trend decomposition using Loess) addresses all three limitations:

from statsmodels.tsa.seasonal import STL

stl = STL(
    series,
    period=12,
    seasonal=13,    # must be odd; controls smoothness of seasonal component
    trend=25,       # must be odd; controls smoothness of trend
    robust=True,    # reduces influence of outliers
)
result = stl.fit()

fig = result.plot()

Key advantages of STL:

  • The seasonal component can change over time — it is re-estimated locally at each point.
  • Robustness to outliers — the robust=True option down-weights anomalous points.
  • Full coverage — no missing values at the edges.

Tuning STL parameters

  • seasonal: higher values = smoother (more rigid) seasonal pattern. Lower values let the seasonal shape evolve faster. Must be odd and ≥ 7.
  • trend: higher values = smoother trend. Should be at least 1.5 × period / (1 − 1.5/seasonal) but the default usually works.
  • robust: set to True when your data has outliers or sudden spikes.

Interpreting decomposition results

The trend tells you direction

A flat trend means the series is stationary in the long run. A bending trend suggests acceleration or deceleration. A sudden kink might indicate a structural change (new competitor, policy change, pandemic).

The seasonal component reveals cycles

The seasonal panel shows the repeating pattern stripped of everything else. Compare its amplitude to the trend — if the seasonal component is 5% of the trend, seasonality is weak. If it is 50%, seasonal planning is critical.

Residuals reveal the unexpected

Good decomposition leaves residuals that look like random noise — no patterns, no trends. If you see structure in the residuals, the decomposition model is missing something (wrong period, wrong model type, or a second seasonal pattern).

from statsmodels.tsa.stattools import acf

# Check if residuals are random
residual_acf = acf(result.resid.dropna(), nlags=30)
# Values should be close to zero at all lags

Practical applications

  • Forecasting: remove the seasonal component, forecast the deseasoned data, then add seasonality back.
  • Anomaly detection: flag points where the residual exceeds a threshold (e.g., 3 standard deviations).
  • Feature engineering: use trend slope and seasonal strength as features for machine learning models.
  • Reporting: show stakeholders the trend line instead of noisy raw data for clearer business narratives.

Common misconception

People often assume that choosing the right decomposition period is obvious. It is not always 12 for monthly data. A retail business might have period=7 (weekly cycle) on daily data plus period=365 (yearly cycle). STL handles only one period — for multiple, you need MSTL or manual iterative decomposition.

The one thing to remember: Decomposition is not a forecasting method — it is an analysis method that reveals the hidden structure of your data, and choosing between classical and STL decomposition (and additive vs. multiplicative) determines how much of that structure you actually see.

pythontime-seriesseasonal-decompositiondata-analysis

See Also