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/2observations. - 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=Trueoption 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 toTruewhen 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.
See Also
- Python Arima Forecasting How ARIMA models use patterns in past numbers to predict the future, explained like a bedtime story.
- Python Autocorrelation Analysis How today's number is connected to yesterday's, and why that connection is the secret weapon of time series analysis.
- Python Exponential Smoothing How exponential smoothing weighs recent events more heavily to predict what happens next, like trusting fresh memories more than old ones.
- Python Multivariate Time Series Why tracking multiple things at once gives you better predictions than tracking each one alone.
- Python Prophet Forecasting How Facebook's Prophet tool predicts the future by breaking data into easy-to-understand pieces.