Fundamentals 14 min read

Lesson 4: Breaking Down Time Series into Trend, Seasonality, Cycle & Random Noise

This tutorial explains the four fundamental components of a time series—trend, seasonality, cyclicality, and random noise—covers additive and multiplicative decomposition models, provides step‑by‑step Python code with visualizations, and shows how removing these components improves stationarity for forecasting.

xkx's Tech General Store
xkx's Tech General Store
xkx's Tech General Store
Lesson 4: Breaking Down Time Series into Trend, Seasonality, Cycle & Random Noise

In the fourth lesson of the time‑series series, we first clarify that a regular time series is composed of four parts: a long‑term trend (T), fixed‑interval seasonality (S), longer‑term cyclic patterns (C), and pure random fluctuations (R). A table lists each component, its English symbol, core characteristics, typical cycle length, and concrete examples such as yearly revenue growth (trend), daily traffic peaks (seasonality), economic cycles (cyclicality), and occasional spikes (random noise).

Common confusion: seasonality repeats with a fixed period (e.g., daily, weekly, yearly), while cyclicality has an irregular period (often 2‑10 years). In most business analytics and monitoring tasks, trend plus seasonality dominate.

Two classic decomposition models:

Additive model : Y(t) = T(t) + S(t) + C(t) + R(t). Suitable when component amplitudes are roughly constant regardless of the series level. Example use cases include temperature variations and CPU usage.

Multiplicative model : Y(t) = T(t) × S(t) × C(t) × R(t). Chosen when fluctuations scale with the series magnitude, such as retail sales where high revenue amplifies seasonal swings.

When using the multiplicative model, all data must be positive; a simple shift (adding a constant) can satisfy this requirement.

Python hands‑on demonstration

We import the necessary libraries and configure Matplotlib to display Chinese characters and minus signs correctly:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import seasonal_decompose
plt.rcParams["font.sans-serif"] = ["PingFang SC", "Noto Sans CJK SC", "Microsoft YaHei", "SimHei", "Arial Unicode MS", "DejaVu Sans"]
plt.rcParams["axes.unicode_minus"] = False

Next, we generate a synthetic three‑year daily series that combines a linear trend, two seasonal patterns (annual and weekly), and Gaussian noise:

dates = pd.date_range(start="2020-01-01", periods=365*3, freq="D")
rng = np.random.default_rng(42)
t = np.arange(len(dates))
trend = 0.05 * t
seasonality = 10 * np.sin(2*np.pi*t/365.25) + 3 * np.sin(2*np.pi*t/7)
noise = rng.normal(0, 2, len(dates))
ts_values = trend + seasonality + noise
ts = pd.Series(ts_values, index=dates, name="synthetic_ts")
plt.figure(figsize=(14,6))
plt.plot(ts, linewidth=0.8)
plt.title("Synthetic Time Series with Trend and Seasonality")
plt.xlabel("Date")
plt.ylabel("Value")
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

We then inspect basic statistics (frequency, length, mean, std) to confirm the series shape.

Additive decomposition (period = 365 days):

decomp_add = seasonal_decompose(ts, model="additive", period=365)
fig, axes = plt.subplots(4,1, figsize=(14,12), sharex=True)
decomp_add.observed.plot(ax=axes[0], title="Original Series")
decomp_add.trend.plot(ax=axes[1], title="Trend Component")
decomp_add.seasonal.plot(ax=axes[2], title="Seasonal Component")
decomp_add.resid.plot(ax=axes[3], title="Residual (Random Noise)")
for ax in axes:
    ax.grid(True, alpha=0.25)
plt.tight_layout()
plt.show()

The four panels correspond to the original series, the smooth upward trend, the repeating seasonal wave, and the scattered residuals. If residuals show patterns, the decomposition is incomplete.

Multiplicative decomposition requires all values to be positive, so we shift the series:

ts_positive = ts - ts.min() + 1
decomp_mult = seasonal_decompose(ts_positive, model="multiplicative", period=365)
fig = decomp_mult.plot()
fig.suptitle("Multiplicative Decomposition Result", y=0.995)
fig.set_size_inches(14,10)
plt.tight_layout()
plt.show()

Finally, we explore stationarity by removing the trend component and comparing rolling statistics before and after detrending:

# Detrend
detrended = ts - decomp_add.trend
# Plot original vs detrended with 30‑day rolling mean/std
fig, axes = plt.subplots(2,2, figsize=(14,8))
# Original series
ts.plot(ax=axes[0,0], title="Original (Non‑stationary)")
axes[0,0].axhline(ts.mean(), color="r", linestyle="--")
# Detrended series
detrended.dropna().plot(ax=axes[0,1], title="Detrended (More Stationary)")
axes[0,1].axhline(detrended.mean(), color="r", linestyle="--")
# Rolling stats for original
rolling_mean = ts.rolling(window=30).mean()
rolling_std = ts.rolling(window=30).std()
ts.plot(ax=axes[1,0], alpha=0.6, title="Original – 30‑day Rolling")
rolling_mean.plot(ax=axes[1,0], label="30‑day Mean", color="red")
rolling_std.plot(ax=axes[1,0], label="30‑day Std", color="green")
axes[1,0].legend()
# Rolling stats for detrended
d_clean = detrended.dropna()
rolling_mean_s = d_clean.rolling(window=30).mean()
rolling_std_s = d_clean.rolling(window=30).std()
d_clean.plot(ax=axes[1,1], alpha=0.6, title="Detrended – 30‑day Rolling")
rolling_mean_s.plot(ax=axes[1,1], label="30‑day Mean", color="red")
rolling_std_s.plot(ax=axes[1,1], label="30‑day Std", color="green")
axes[1,1].legend()
for ax in axes.ravel():
    ax.grid(True, alpha=0.25)
plt.tight_layout()
plt.show()

Observations:

The original series shows a rising rolling mean, confirming non‑stationarity.

After removing the trend, the rolling mean and standard deviation hover around constant levels, indicating much improved stationarity.

These steps tie back to earlier lessons on differencing, detrending, and seasonal decomposition, all aimed at converting a non‑stationary series into a stationary one suitable for AR, ARMA, or other traditional forecasting models.

Overall workflow:

Apply additive or multiplicative decomposition to split the series into trend, seasonality, cycle, and residual.

Remove the deterministic components (trend, seasonality, cycle) to obtain a stationary residual series.

Model each component separately if needed, then recombine (add or multiply) the forecasts to produce the final prediction.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Pythontime seriesstatsmodelsseasonalitytrendadditive decompositionmultiplicative decomposition
xkx's Tech General Store
Written by

xkx's Tech General Store

Code with the left hand, enjoy with the right; a keystroke sweeps away worries.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.