Part II: Classical Forecasting and Time Series Analysis
Chapter 3: Statistical Foundations

Trend and Seasonality

"They keep calling me noise. I am not noise. I arrive every December, on schedule, with a peak you could set your calendar by. Subtract me and you will finally see what is actually going on."

A Seasonal Component That Peaks Punctually Every December
Big Picture

A time series is rarely one thing; it is a sum (or product) of slow, fast, and repeating behaviors stacked on top of each other, and almost every forecasting method works by pulling those layers apart, modeling each, and putting them back together. This section names the layers, the level, the trend, the seasonality, the cycle, and the remainder, and gives you the two ways they combine: additively when the seasonal swing has a fixed size, multiplicatively when the swing grows with the level. It then treats the two most consequential layers in depth. Trend can be a smooth function of time you may extrapolate, or a random walk you may not; confusing them is one of the costliest mistakes in forecasting. Seasonality can be encoded as one indicator per season or, far more economically, as a handful of sine and cosine harmonics, which is the only practical way to handle the daily, weekly, and yearly cycles that overlap in modern high-frequency data. By the end you will decompose an hourly sensor series with three seasonalities and a trend, subtract the structure, and read the remainder, the exact setup the decomposition machinery of Section 3.5 and the seasonal models of Chapter 5 are built on.

The previous section, Section 3.3, studied autocorrelation and the way a series remembers its own past. That memory is what makes the layers of a series visible: a slow trend shows up as autocorrelation that decays almost not at all, and a seasonal cycle shows up as autocorrelation that spikes at the season length. Here we give those layers names and a generative model. The organizing idea is simple and runs through the whole chapter: before you model the unpredictable part of a series you must first account for the parts that are entirely predictable, the steady climb and the calendar-driven swing, because a model asked to learn structure it could have been told will spend its capacity badly and forecast worse.

1. The Components of a Series Beginner

Classical time-series analysis treats an observed series $X_t$ as the visible result of several unobserved components that evolve on different timescales. There are five worth naming. The level is the baseline value the series fluctuates around at a given moment. The trend $T_t$ is the long-run direction, the slow climb or decline over many periods. The seasonality $S_t$ is the pattern that repeats over a fixed, known period: a day, a week, a year. The cycle is a longer wave-like swing whose period is not fixed and not known in advance, the business cycle being the classic example, which is precisely what distinguishes it from seasonality. The remainder $R_t$ (also called the irregular or residual component) is what is left once the structured parts are removed, the part this chapter's stationary-process machinery from Section 3.2 will model. In most decompositions the cycle is folded into the trend, leaving the trio of trend, seasonality, and remainder that organizes the rest of this section.

The components must be combined into the observation somehow, and there are two dominant rules. The additive model adds them,

$$X_t = T_t + S_t + R_t,$$

and the multiplicative model multiplies them,

$$X_t = T_t \cdot S_t \cdot R_t.$$

The distinction is not cosmetic. In the additive model the seasonal swing has a constant absolute size: if December always adds two hundred units, it adds two hundred whether the level is one thousand or ten thousand. In the multiplicative model the seasonal swing is a constant fraction of the level: if December always adds twenty percent, the absolute size of the December bump grows as the series grows. The deciding question is empirical and visual, illustrated in Figure 3.4.1: does the height of the seasonal wiggle stay flat as the series rises, or does it fan out? Flat means additive; fanning means multiplicative.

Additive: constant swing time → Multiplicative: swing grows time →
Figure 3.4.1: The visual test for composition. On the left the series trends upward but every seasonal up-and-down has the same height, so an additive model fits and the seasonal term is a fixed offset. On the right the seasonal swings widen as the series climbs, the signature of multiplicative composition where the seasonal term is a fixed proportion of the level. Taking the logarithm of the right-hand series turns its widening swings into constant ones, which is why a log transform converts multiplicative structure into additive.

The link between the two models is the logarithm, and this is the single most useful fact in the section. Taking logs of the multiplicative model turns the product into a sum,

$$\log X_t = \log T_t + \log S_t + \log R_t,$$

so a multiplicative series becomes additive in log space. This is exactly why analysts log-transform series whose variance grows with their level (sales, populations, prices, energy demand): the transform stabilizes the variance and lets every additive tool apply. We will use it directly in the worked example of subsection five.

Key Insight: Variance That Grows With Level Is the Tell for Multiplicative or Log

You do not choose additive versus multiplicative by taste; the data tells you. Plot the series and watch the seasonal amplitude. If the up-and-down swings keep a constant height as the level rises, the structure is additive and you model it directly. If the swings fan out in proportion to the level, the structure is multiplicative, and the cleanest fix is almost never to fit a product model by hand: take the logarithm, which collapses the multiplication into addition and the growing variance into a constant one, then use every additive method unchanged and exponentiate at the end. Growing-with-level variance is the diagnostic; the log transform is the cure.

2. Trend: Deterministic or Stochastic Intermediate

The word "trend" hides a deep and dangerous ambiguity, and resolving it is the most important idea in this subsection. A deterministic trend is a fixed function of the clock: the mean of the series is literally $\mu_t = f(t)$ for some known form, most simply a line $\mu_t = \beta_0 + \beta_1 t$, and the random part fluctuates around it,

$$X_t = \beta_0 + \beta_1 t + \varepsilon_t.$$

Here the future is anchored: at any horizon the expected level is $\beta_0 + \beta_1 t$ and the uncertainty band has constant width. A stochastic trend is the opposite. It is a trend with no fixed equation, produced by accumulating random shocks, the random walk of Section 3.2,

$$X_t = X_{t-1} + \delta + \varepsilon_t = X_0 + \delta t + \sum_{s=1}^{t} \varepsilon_s,$$

which carries a unit root. Both can look like a rising line on a chart, yet they behave in opposite ways and demand opposite treatment. The deterministic trend returns to its line after a shock; the stochastic trend never returns, because each shock $\varepsilon_s$ is permanently baked into the running sum and the variance $\operatorname{Var}(X_t) = \sigma^2 t$ grows without bound. The right cure differs accordingly: you remove a deterministic trend by subtracting the fitted function (detrending by regression), and you remove a stochastic trend by differencing, $\nabla X_t = X_t - X_{t-1}$, which turns the random walk back into stationary noise. Apply the wrong cure and you make things worse, as the warning below records.

Warning: Extrapolating a Deterministic Trend, or Detrending a Stochastic One

Two symmetric errors follow from confusing the trend types. Fit a straight line to a series that is really a random walk and extrapolate it, and you project a confident, narrow forecast cone for a process whose true uncertainty fans out as $\sqrt{t}$: the forecast looks precise and is wildly overconfident, the textbook way to predict a stock price and be humiliated. Conversely, difference a series that has only a gentle deterministic trend and you have over-differenced, injecting a unit root into the moving-average part and inflating short-horizon variance. The unit-root tests of Section 3.2 (augmented Dickey-Fuller, KPSS) exist precisely to tell the two apart before you commit to a cure. Never read the trend type off the chart alone.

Estimating a trend, once you know which kind you have, is straightforward. A deterministic trend is fit by regression on time (a line, a polynomial, or a spline). Either kind can be smoothed out of view by a moving average, which estimates the local level by averaging a window of neighbors. A centered moving average of odd window $2k+1$ is

$$\hat T_t = \frac{1}{2k+1} \sum_{j=-k}^{k} X_{t+j},$$

and its window length is the key knob: too short and seasonality bleeds through, too long and genuine bends in the trend are flattened. The standard trick for an even seasonal period $m$ (so the window cannot be centered on an integer) is the $m$-term centered moving average that half-weights the two endpoints, which we will use in the next section's classical decomposition. Code 3.4.1 fits a linear deterministic trend by least squares and estimates the same trend with a moving average, then contrasts how each behaves on a noisy upward series.

import numpy as np

rng = np.random.default_rng(7)
T = 120
t = np.arange(T)
# A genuine deterministic line plus noise: the trend is a fixed function of t.
true_trend = 5.0 + 0.30 * t
x = true_trend + rng.normal(0, 4.0, size=T)

# (a) Deterministic trend by least-squares regression on time.
A = np.column_stack([np.ones(T), t])            # design matrix [1, t]
beta, *_ = np.linalg.lstsq(A, x, rcond=None)    # beta = [intercept, slope]
trend_reg = A @ beta
print(f"fitted intercept={beta[0]:.2f}, slope={beta[1]:.3f}")

# (b) Trend by a centered moving average of window 2k+1.
def centered_ma(series, k):
    out = np.full_like(series, np.nan, dtype=float)
    for i in range(k, len(series) - k):
        out[i] = series[i - k : i + k + 1].mean()
    return out

trend_ma = centered_ma(x, k=6)                  # window 13
# Compare each estimate to the known truth on the interior points.
interior = slice(6, T - 6)
print(f"regression RMSE vs truth: {np.sqrt(np.mean((trend_reg[interior]-true_trend[interior])**2)):.3f}")
print(f"moving-avg  RMSE vs truth: {np.sqrt(np.nanmean((trend_ma[interior]-true_trend[interior])**2)):.3f}")
Code 3.4.1: Two trend estimators on the same series, a parametric least-squares line and a nonparametric centered moving average. The regression recovers a global slope and extrapolates; the moving average tracks local level but cannot forecast past the data and loses $k$ points at each end.
fitted intercept=5.36, slope=0.297
regression RMSE vs truth: 0.74
moving-avg  RMSE vs truth: 1.36
Output 3.4.1: Because the true trend really is a line, the parametric regression wins (RMSE 0.74 against the truth) and can be extrapolated; the moving average is more flexible but noisier (RMSE 1.36) and is silent outside the data. When the trend is not a known shape, that flexibility is exactly what you want, which is the trade-off the next section formalizes.
Numeric Example: A Two-Hundred-Unit Bump, Additive or Multiplicative?

A retailer records December sales of $1{,}200$ units in a year whose non-December months average $1{,}000$, so December adds $200$ units on a base of $1{,}000$, a $20\%$ lift. Three years later, after steady growth, the non-December base is $2{,}000$. Under an additive seasonal model the December bump is a fixed $+200$, predicting December sales of $2{,}200$. Under a multiplicative model the bump is a fixed $+20\%$, predicting $2{,}000 \times 1.20 = 2{,}400$. The two models disagree by $200$ units, and the disagreement grows with the level. If the firm's actual December came in near $2{,}400$, the multiplicative model is right and the additive one would systematically under-forecast every future December. Equivalently, $\log(2400/2000) = \log(1.20) \approx 0.182$ matches $\log(1200/1000)$ to the digit, confirming that in log space the seasonal effect is the same constant in both years, the algebraic fingerprint of multiplicative seasonality.

3. Seasonality: Dummies and Fourier Harmonics Intermediate

Seasonality is a pattern that repeats over a fixed, known period $m$: $m=7$ for day-of-week structure in daily data, $m=24$ for hour-of-day structure in hourly data, $m=12$ for month-of-year structure in monthly data. Two representations dominate, and the choice between them governs how many parameters you spend. The first is seasonal dummies: one indicator variable per season, so the model carries a separate, freely estimated offset for each of the $m$ phases,

$$X_t = T_t + \sum_{j=1}^{m-1} \gamma_j \, D_{j,t} + R_t,$$

where $D_{j,t}$ is one when $t$ falls in season $j$ and zero otherwise (one dummy is dropped to avoid collinearity with the intercept). Dummies are maximally flexible, capturing any seasonal shape, but they cost $m-1$ parameters, which is fine for $m=12$ and ruinous for $m=365$ or $m=168$ (the weekly cycle in hourly data).

A round cartoon character cycles around four seasonal scenes, swelling up bundled in winter and shrinking under summer sun, then swelling again the next winter, personifying a seasonal component that peaks punctually at the same point every cycle.
Figure 3.4.2: Seasonality is the character who swells punctually every winter and shrinks every summer: a deterministic surface pattern that repeats on the calendar, which you can strip off before modeling whatever wriggles underneath.

Whichever encoding you pick, the thing being encoded is the punctual, calendar-locked swing that Figure 3.4.2 personifies: a component that peaks at the same phase every cycle, ready to be subtracted before the remainder is modeled.

The second representation is Fourier terms, and it is the workhorse for long and multiple seasonalities. Any periodic shape can be approximated by a sum of sines and cosines at harmonic frequencies of the base period. Using $K$ harmonics for a season of length $m$,

$$S_t = \sum_{k=1}^{K} \left[ a_k \sin\!\left( \frac{2 \pi k t}{m} \right) + b_k \cos\!\left( \frac{2 \pi k t}{m} \right) \right],$$

which costs only $2K$ parameters, and $K$ can be far smaller than $m$ when the seasonal shape is smooth. A yearly cycle in daily data ($m=365$) is often captured well by $K=4$ to $10$ harmonics, eight to twenty parameters instead of three hundred sixty-four. The smoothness is the catch: Fourier terms impose a smooth periodic shape and cannot reproduce a spike that lasts a single season, where a dummy can. The two are complementary, not rivals.

The decisive advantage of Fourier terms appears with multiple seasonalities, which are everywhere in modern data. An hourly electricity or sensor series has a daily cycle ($m_1 = 24$), a weekly cycle ($m_2 = 168$), and a yearly cycle ($m_3 = 8766$) layered on top of one another. Dummies cannot handle this gracefully; Fourier terms simply add one harmonic block per period,

$$S_t = \sum_{p} \sum_{k=1}^{K_p} \left[ a_{p,k} \sin\!\left( \frac{2 \pi k t}{m_p} \right) + b_{p,k} \cos\!\left( \frac{2 \pi k t}{m_p} \right) \right],$$

each with its own period $m_p$ and its own number of harmonics $K_p$. This additive-in-frequency construction is exactly what Prophet and the TBATS family use under the hood, and it is the engine of the worked example. The frequency-domain reading of these harmonics, why each $(\sin, \cos)$ pair is a single tone in a spectrum, is the subject of Chapter 4.

Fun Fact: Three Sine Waves Beat Three Hundred Switches

A full year of daily seasonality, modeled with one switch per day, needs three hundred sixty-four dummy variables, one fewer than the calendar to avoid colliding with the intercept. The same yearly shape, smoothed into sines and cosines, usually needs about ten parameters to look indistinguishable to the eye and forecasts better because it does not overfit each individual day's noise. The Fourier representation is not just cheaper; it borrows strength across neighboring days, so a quiet Tuesday in March is informed by the Mondays and Wednesdays around it instead of being estimated from one column in isolation.

Beyond these regular cycles sit calendar and holiday effects: a public holiday, a sales event, the leap-year shift, the moving date of Easter or Ramadan. These do not fit a fixed period and are handled as explicit regressors (holiday indicator columns), which Fourier terms and dummies alike accommodate by simply appending the column.

4. Removing Components Before Modeling the Remainder Intermediate

Why pull the series apart at all? Because the remainder is the only part worth handing to a stochastic model, and it is hidden until the structured parts are gone. The strategy has a name, decompose-then-model, and it underlies the entire classical pipeline: estimate and remove the trend, estimate and remove the seasonality, and feed the stationary remainder to an ARMA-style model that captures the short-range dependence the structured terms cannot. There are two distinct ways to remove each component, and they are not interchangeable.

Detrending removes the trend, either by subtracting a fitted deterministic function (regression detrending) or by first differencing $\nabla X_t = X_t - X_{t-1}$ to kill a stochastic trend, the unit-root cure of subsection two. Seasonal differencing removes seasonality by subtracting the value one full season ago,

$$\nabla_m X_t = X_t - X_{t-m},$$

which annihilates a constant seasonal pattern just as ordinary differencing annihilates a constant drift. This is distinct from seasonal adjustment, which estimates the seasonal component $\hat S_t$ and subtracts it, $X_t - \hat S_t$, leaving the trend intact rather than differencing it away. The distinction matters: seasonal differencing removes both the trend and the season in one stroke and changes the data's interpretation (you are now modeling year-over-year changes); seasonal adjustment isolates and removes only the season, preserving the level and trend you may still want to forecast. The seasonal-difference operator $\nabla_m$ is the seasonal half of the SARIMA models in Chapter 5; the estimate-and-subtract route is the path the decomposition methods of Section 3.5 take.

Key Insight: Separate the Predictable Before You Model the Unpredictable

Trend and seasonality are, by definition, the parts of a series you can predict from the calendar and the clock alone. A forecasting model that is forced to relearn them from scratch wastes its capacity and is prone to extrapolating them badly. The discipline that pays off everywhere from classical SARIMA to deep forecasters is the same: subtract the structure you already understand, model only the residual mystery, and add the structure back at forecast time. Decomposition is not a preprocessing nicety; it is the act of telling the model what it does not need to learn, so it can spend its power on what it does.

5. Worked Example: Detrending an Hourly Sensor Series With Multiple Seasonalities Advanced

We now run the full pipeline on the sensor/IoT running dataset introduced in Chapter 2: an hourly telemetry stream that carries a slow upward trend (a sensor drifting as it ages), a strong daily cycle ($m=24$), and a weekly cycle ($m=168$), all buried in noise. The plan is exactly the decompose-then-model recipe: build a design matrix of a linear trend plus Fourier harmonics for both seasonalities, fit it by least squares, subtract the fitted structure, and inspect the remainder to confirm the seasonality is gone. Code 3.4.2 implements the Fourier seasonal regression entirely from scratch so every column of the design matrix is visible.

import numpy as np

rng = np.random.default_rng(11)
N = 24 * 7 * 8                       # eight weeks of hourly data
t = np.arange(N)

# --- Synthesize a series with trend + daily(24) + weekly(168) + noise ---
trend  = 20.0 + 0.004 * t
daily  = 6.0 * np.sin(2*np.pi*t/24)  + 2.0 * np.cos(2*np.pi*2*t/24)   # 2 harmonics
weekly = 4.0 * np.cos(2*np.pi*t/168) + 1.5 * np.sin(2*np.pi*2*t/168)  # 2 harmonics
x = trend + daily + weekly + rng.normal(0, 1.0, size=N)

# --- Build Fourier design columns for one seasonal period ---
def fourier_terms(t, period, K):
    cols = []
    for k in range(1, K + 1):
        cols.append(np.sin(2*np.pi*k*t/period))
        cols.append(np.cos(2*np.pi*k*t/period))
    return np.column_stack(cols)

# Design matrix: [intercept, t, daily harmonics, weekly harmonics]
A = np.column_stack([
    np.ones(N), t,
    fourier_terms(t, 24, K=3),       # daily, 3 harmonics -> 6 columns
    fourier_terms(t, 168, K=3),      # weekly, 3 harmonics -> 6 columns
])
beta, *_ = np.linalg.lstsq(A, x, rcond=None)   # one least-squares solve
fitted = A @ beta
remainder = x - fitted                          # decompose-then-inspect

print(f"design matrix shape: {A.shape}")        # (N, 14)
print(f"recovered trend slope: {beta[1]:.4f}  (true 0.0040)")
print(f"remainder std: {remainder.std():.3f}  (noise std was 1.0)")
# A clean decomposition leaves almost no daily structure in the remainder:
lag24_corr = np.corrcoef(remainder[24:], remainder[:-24])[0, 1]
print(f"remainder autocorr at lag 24: {lag24_corr:.3f}  (near 0 = season removed)")
Code 3.4.2: From-scratch Fourier seasonal regression. One design matrix holds an intercept, a linear trend column, and three harmonics each for the daily and weekly periods; a single least-squares solve recovers all of them, and subtracting the fit yields the remainder we then test for leftover seasonality.
design matrix shape: (1344, 14)
recovered trend slope: 0.0040  (true 0.0040)
remainder std: 0.987  (noise std was 1.0)
remainder autocorr at lag 24: -0.012  (near 0 = season removed)
Output 3.4.2: The regression recovers the trend slope to four decimals, drives the remainder's standard deviation down to the true noise level of one, and leaves essentially zero autocorrelation at lag 24, the proof that the daily season has been absorbed. The remainder is now the stationary mystery ready for an ARMA model.
Numeric Example: Counting the Parameters Fourier Saves

The series above has a daily period of $24$ and a weekly period of $168$. Encoded with seasonal dummies, the daily cycle would cost $24 - 1 = 23$ parameters and the weekly cycle $168 - 1 = 167$, a total of $190$ seasonal parameters before the trend is even counted, and the two dummy blocks would partly collide because every hour is also inside some week. The Fourier construction in Code 3.4.2 spends $2K$ per period: $2 \times 3 = 6$ for the daily cycle and $6$ for the weekly cycle, $12$ seasonal parameters in all. That is a reduction from $190$ to $12$, roughly a factor of sixteen fewer parameters, with no collision because the sine and cosine columns for different periods are nearly orthogonal. Fewer parameters on the same data means lower variance in the estimates and a remainder you can trust.

The from-scratch version makes the mechanics transparent, but in practice you reach for a library that builds the Fourier features, fits the regression, and forecasts in a handful of lines. Code 3.4.3 reproduces the decomposition with statsmodels for the ordinary-least-squares fit and a compact Fourier helper, the production path.

import numpy as np
import pandas as pd
import statsmodels.api as sm
from statsmodels.tsa.deterministic import DeterministicProcess, Fourier

N = 24 * 7 * 8
idx = pd.RangeIndex(N)
# (reuse x from Code 3.4.2 as the observed series)
series = pd.Series(x, index=idx)

# DeterministicProcess assembles trend + multiple Fourier seasonalities for you.
dp = DeterministicProcess(
    index=idx, constant=True, order=1,                    # intercept + linear trend
    additional_terms=[Fourier(24, order=3),               # daily, 3 harmonics
                      Fourier(168, order=3)],             # weekly, 3 harmonics
)
design = dp.in_sample()
fitted = sm.OLS(series, design).fit()
remainder = series - fitted.predict(design)
print(f"remainder std: {remainder.std():.3f}")
Code 3.4.3: The same trend-plus-two-seasonalities decomposition via statsmodels. DeterministicProcess with two Fourier terms builds the entire design matrix, and OLS fits it, replacing the hand-built columns and manual solve of Code 3.4.2.
Library Shortcut: Trend and Multi-Seasonal Fourier Features in One Object

The from-scratch design matrix of Code 3.4.2 is about a dozen lines of column-building and a manual least-squares solve; statsmodels' DeterministicProcess with Fourier terms collapses that to roughly four lines (Code 3.4.3), and it handles the harmonic bookkeeping, the orthogonalization, and out-of-sample feature generation (dp.out_of_sample()) that the hand-rolled version omits. Going one level higher, Prophet exposes the same additive trend-plus-Fourier-seasonality model behind a fit/predict pair that also bakes in holiday regressors and changepoint-based trend flexibility, and Nixtla's statsforecast ships MSTL and a multi-seasonal Fourier regression that scale to thousands of series. Each step up the ladder trades a few lines of your code for a tested implementation of the exact math in this section; the from-scratch version exists so you can read what the one-liner is doing.

Practical Example: The Drifting Sensor That Looked Like a Failing Machine

Who: A reliability engineer on a factory condition-monitoring team, watching an hourly vibration sensor on a critical pump.

Situation: The raw sensor reading had been climbing steadily for weeks and now crossed the fixed alarm threshold every afternoon, firing dozens of pages a day and threatening an expensive unplanned shutdown.

Problem: Was the pump actually degrading, or was the signal dominated by structure that had nothing to do with the pump's health? The afternoon alarms were suspiciously regular.

Dilemma: Shutting down a healthy pump costs a day of production; ignoring a real fault risks catastrophic failure. The team needed to separate genuine anomaly from predictable pattern before deciding.

Decision: Before trusting the raw threshold, they decomposed the signal exactly as in Code 3.4.2: a slow linear trend, a daily Fourier cycle, and a weekly Fourier cycle, fit and subtracted.

How: The decomposition revealed that almost all of the afternoon spiking was a daily thermal cycle (the plant warmed in the afternoon, raising baseline vibration) plus a slow sensor drift from aging electronics, not the pump. The slope was tiny and the daily harmonic was large. After removing trend and both seasonalities, the remainder sat flat and well within bounds, with no growing variance that would indicate real degradation.

Result: The team moved the alarm from the raw signal to the decomposed remainder, eliminating the false afternoon pages overnight while preserving sensitivity to true anomalies (a genuine fault would show up as structure in the remainder, not in the known daily cycle). They scheduled a routine sensor recalibration to remove the drift, and the pump kept running.

Lesson: A threshold on a raw series alarms on everything predictable, the trend, the daily cycle, the weekly cycle, none of which is news. Decompose first, then watch the remainder. The anomaly detection of Chapter 8 is built on exactly this principle: you can only see the surprise once you have subtracted the expected.

Research Frontier: Seasonality in Deep and Foundation Forecasters (2024 to 2026)

The decompose-then-model idea did not stay classical; it has become an architectural prior in modern forecasters. The seasonal-trend decomposition block inside Autoformer and the explicit trend-and-seasonal heads of DLinear and NLinear (2022 onward) showed that an extremely simple linear model wrapped around a moving-average decomposition rivals far larger Transformers on long-horizon benchmarks, which sharpened the field's attention on getting trend and seasonality right rather than stacking attention. Through 2024 to 2026 the theme deepens in two directions. First, temporal foundation models, Chronos, TimesFM, Moirai, and Lag-Llama, are evaluated on how well they handle multiple and shifting seasonalities zero-shot, and a recurring finding is that explicit seasonal features or decomposition front-ends still help even very large pretrained models on strongly periodic series. Second, multi-seasonal classical methods, the MSTL decomposition and the TBATS family, remain competitive and fast baselines for high-frequency data with daily, weekly, and yearly cycles, and 2024 to 2026 tooling (Nixtla's statsforecast, sktime) makes them trivial to run at scale, so they are the honest baseline every neural seasonal model must beat. The frontier message echoes subsection four: the more powerful the model, the more it benefits from being told what the calendar already knows.

Exercise 3.4.1: Additive or Multiplicative? Conceptual

For each series, state whether you expect additive or multiplicative composition and justify it in two sentences using the variance-with-level test of the Key Insight in subsection one. (a) Monthly airline passenger counts that have grown tenfold over thirty years, with summer peaks that are visibly larger in recent years than in early years. (b) Daily average temperature in a city, with a yearly cycle whose amplitude is stable across decades. (c) Quarterly revenue of a fast-growing startup with a strong Q4 holiday spike. For any series you label multiplicative, write the transform that would let you model it with additive tools.

Exercise 3.4.2: Build and Test a Two-Seasonality Decomposition Coding

Starting from Code 3.4.2, regenerate the hourly series but add a third, yearly-style seasonality at period $m=672$ (four weeks, to keep the run short) with amplitude $3.0$. Extend the design matrix with a third fourier_terms block for that period, refit, and confirm the remainder's autocorrelation is near zero at lags $24$, $168$, and $672$. Then deliberately omit the weekly block from the design, refit, and report what the lag-$168$ autocorrelation of the remainder becomes. Explain in two sentences why a missing seasonal block leaves a telltale autocorrelation spike at its period.

Exercise 3.4.3: How Many Harmonics Is Enough? Analysis

Using the series from Code 3.4.2, sweep the number of daily harmonics $K \in \{1, 2, 3, 5, 8\}$ and, for each, record the remainder's standard deviation and the number of seasonal parameters spent. Plot remainder standard deviation against parameter count and identify the elbow where adding harmonics stops helping. Relate your elbow to the true generating process (the daily signal in the code used only two harmonics) and argue, in a short paragraph, why pushing $K$ far past the elbow risks fitting noise, connecting this to the bias-variance discussion you will meet again in model selection in Chapter 5.

Exercise 3.4.4: Deterministic Versus Stochastic Trend Open-ended

Simulate two series of length $300$: one with a deterministic linear trend plus white noise, and one a random walk with the same average drift (a stochastic trend), tuned so both reach a similar endpoint. Fit a straight line to each and extrapolate twenty steps with a forecast interval. Discuss why the interval is honest for the first series and dangerously narrow for the second, then propose which cure (regression detrending versus differencing) you would apply to each and how a unit-root test from Section 3.2 would have told you which is which before you ever drew the line.