Part II: Classical Forecasting and Time Series Analysis
Chapter 5: Univariate Forecasting Models

Exponential Smoothing and ETS

"I keep a single number that is the whole of my memory. Each morning I nudge it a little toward what I just saw and let the rest dissolve. The distant past is not deleted; it is merely outvoted, geometrically, forever."

An Exponential Smoother Gently Forgetting the Past
Big Picture

Exponential smoothing forecasts the future by maintaining a small set of running states (a level, an optional trend, an optional seasonal pattern) and updating each one as a weighted compromise between the newest observation and its own previous value, so that the influence of any past point decays geometrically into irrelevance. From the one-line recursion of simple exponential smoothing grows a family that adds a trend (Holt), damps that trend to keep long forecasts sane, and layers on additive or multiplicative seasonality (Holt-Winters). The modern repackaging of that family is ETS, a taxonomy over the (Error, Trend, Seasonal) components backed by a state-space model whose likelihood lets the computer choose the right member automatically by AIC. This section builds the recursions from scratch, fits the same models in two library lines, derives the exact equivalence between simple smoothing and ARIMA(0,1,1), and explains why this old, cheap, almost parameter-free family still wins large stretches of the M-competitions against far more elaborate machinery.

The previous sections of this chapter built the autoregressive integrated moving-average models around an explicit stochastic equation and a careful identification ritual of differencing, correlogram reading, and order selection. Exponential smoothing arrives from the opposite direction. It starts not from a generative equation but from an intuition about memory: recent observations should matter more than old ones, and the rate of forgetting should be a tunable knob rather than a hard cutoff. That intuition turns out to have a rigorous state-space model underneath it, the ETS framework, which is why a method that began as a 1950s operations-research heuristic now sits in the same statistical company as ARIMA. We assume the stationarity vocabulary of Chapter 3 and the differencing and ARIMA machinery developed earlier in this chapter, and we use the level, trend, and seasonal notation collected in the unified table of Appendix A. The exponentially weighted moving average we met as a feature transform back in Chapter 2 is, as we will see immediately, exactly the forecasting rule of simple exponential smoothing wearing a different hat.

By the end of this section you should be able to do four things. First, write down and run the level, trend, and seasonal recursions of the exponential-smoothing family and explain what each smoothing parameter controls. Second, recognize from a plot which member fits (does the series trend? does the seasonal amplitude grow with the level?) and name the corresponding ETS triple. Third, explain why the damped trend, automatic AIC selection, and the state-space backbone make ETS a default-strength method rather than a heuristic. Fourth, state precisely the relationship between exponential smoothing and the ARIMA models of the earlier sections, including where the two families coincide and where they part. The six numbered subsections build to exactly those four competencies, and the final worked example exercises all of them on one series.

1. Simple Exponential Smoothing Beginner

Begin with a series that has no trend and no seasonality, just a slowly wandering level buried in noise. The forecasting question is simply: what is the current level? Simple exponential smoothing (SES) answers it with a single running state $\ell_t$, the smoothed level, updated at each step by the recursion

$$\ell_t \;=\; \alpha\, x_t \;+\; (1 - \alpha)\, \ell_{t-1}, \qquad 0 < \alpha < 1,$$

and it forecasts every future point as the current level, $\hat x_{t+h \mid t} = \ell_t$ for all horizons $h \ge 1$. The update reads as a compromise: the new level is a fraction $\alpha$ of the fresh observation plus the remaining fraction $1 - \alpha$ of where we already thought the level was. The smoothing parameter $\alpha$ is the memory knob. A value near $1$ throws almost all weight on the latest point, producing a nimble but jittery tracker; a value near $0$ barely moves the level, producing a sluggish but stable one. There is a second, equivalent way to write the same update that makes the learning interpretation explicit. Let $e_t = x_t - \ell_{t-1}$ be the one-step forecast error, the surprise. Then

$$\ell_t \;=\; \ell_{t-1} \;+\; \alpha\, e_t,$$

so the level takes a step of size $\alpha$ in the direction of its own most recent mistake. This error-correction form is the seed of every state-space filter in the book: predict, observe the surprise, correct by a gain times the surprise. The Kalman filter of Chapter 7 is the same sentence with a matrix gain. Figure 5.5.2 captures how that running state remembers the past.

A cartoon character walks along a beach leaving footprints where the newest is crisp and each older print fades geometrically faster until the tide washes the oldest away, dramatizing how exponential smoothing weights recent observations heavily and discounts the past geometrically.
Figure 5.5.2: Exponential smoothing remembers like footprints in sand: the latest step is vivid, and every older one fades geometrically until the tide takes it.

One detail the recursion hides is where it starts. The level $\ell_0$ must be initialized before the first update can run, and the choice matters for short series because the initial value's weight $(1-\alpha)^t$ takes a while to decay. The simplest convention sets $\ell_0$ to the first observation $x_1$; a steadier choice averages the first few points; and the principled choice, which the libraries take, treats $\ell_0$ as one more parameter estimated jointly with $\alpha$ by maximizing the likelihood. For a long series all three converge to the same forecast because the influence of $\ell_0$ washes out geometrically, but on a series of length twenty the initialization can move the early fitted values noticeably, so it is worth being deliberate rather than defaulting silently.

Key Insight: One Recursion Is an Infinite Geometric Memory

Unrolling the SES recursion exposes what $\alpha$ really controls. Substituting $\ell_{t-1}$ into $\ell_t$ repeatedly gives

$$\ell_t \;=\; \alpha \sum_{j=0}^{t-1} (1-\alpha)^j\, x_{t-j} \;+\; (1-\alpha)^t\, \ell_0,$$

which is a weighted average of every past observation with weights $\alpha (1-\alpha)^j$ that decay geometrically with age. The weights sum to one (a true average), the most recent point carries weight $\alpha$, the point before it $\alpha(1-\alpha)$, and so on. Nothing is ever forgotten outright; the past is merely discounted at a constant rate. This is precisely the exponentially weighted moving average (EWMA) feature transform of Chapter 2: the SES forecast and the EWMA feature are the same object, one viewed as a prediction and the other as a smoothed input column. The half-life of the memory, the age at which a point's weight has halved, is $\ln(0.5)/\ln(1-\alpha)$ steps.

Because the forecast is flat (a horizontal line at $\ell_t$), SES is honest about its limits: with no trend state it cannot extrapolate a slope, and with no seasonal state it cannot bend. Its job is to estimate the level of a locally constant series as efficiently as possible, and at that job it is excellent and nearly impossible to overfit, with a single parameter to set. The flatness is a feature, not a bug, for the many real series (inventory levels, baseline vital signs, ambient sensor readings between events) whose right one-step model genuinely is "tomorrow looks like a discounted average of recent days." The numeric example below runs the recursion by hand so the geometric decay becomes concrete.

Temporal Thread: One Gain, Many Names

The error-correction line $\ell_t = \ell_{t-1} + \alpha e_t$ is the first appearance of a pattern that recurs through the whole book: maintain a state, predict, measure the surprise $e_t$, and correct by a gain times the surprise. Here the gain is the scalar $\alpha$. In the Kalman filter of Chapter 7 it becomes the Kalman gain, a matrix computed from the innovation covariance rather than fixed by hand. In the recurrent networks of Chapter 10 the same update reappears as a learned gating that decides how much of the new input to admit into the hidden state, and in the structured state-space models of Chapter 13 as a learned transition operator. Exponential smoothing is the simplest possible member of this lineage: the gating is a single number you tune by least squares, and everything more powerful is this idea given more parameters and a way to learn them.

Numeric Example: Three Steps of the Recursion by Hand

Take $\alpha = 0.3$ and an initial level $\ell_0 = 10.0$, then feed observations $x_1 = 12,\ x_2 = 9,\ x_3 = 14$. Step one: $\ell_1 = 0.3 \times 12 + 0.7 \times 10 = 3.6 + 7.0 = 10.6$. Step two: $\ell_2 = 0.3 \times 9 + 0.7 \times 10.6 = 2.7 + 7.42 = 10.12$. Step three: $\ell_3 = 0.3 \times 14 + 0.7 \times 10.12 = 4.2 + 7.084 = 11.284$. The forecast for every future period is the flat line $\hat x_{4 \mid 3} = \hat x_{5 \mid 3} = \cdots = 11.284$. Notice the discounting in action: the weight on $x_1$ inside $\ell_3$ is $0.3 \times 0.7^2 = 0.147$, on $x_2$ it is $0.3 \times 0.7 = 0.21$, and on $x_3$ it is $0.3$, a clean geometric ladder. The half-life here is $\ln(0.5)/\ln(0.7) \approx 1.94$ steps, so a point's influence halves roughly every two observations.

Code 5.5.1 implements SES from scratch and selects $\alpha$ by minimizing the sum of squared one-step errors, the same objective the libraries optimize.

import numpy as np
from scipy.optimize import minimize_scalar

def ses_fit(x, alpha, l0=None):
    """Run simple exponential smoothing; return levels and one-step forecast errors."""
    x = np.asarray(x, dtype=float)
    l = x[0] if l0 is None else l0           # default initial level = first observation
    levels, errors = [], []
    for xt in x:
        e = xt - l                           # one-step surprise BEFORE updating
        l = l + alpha * e                    # error-correction form of the recursion
        levels.append(l)
        errors.append(e)
    return np.array(levels), np.array(errors)

def ses_select_alpha(x):
    """Choose alpha by minimizing the sum of squared one-step forecast errors."""
    def sse(alpha):
        _, e = ses_fit(x, alpha)
        return np.sum(e[1:] ** 2)            # skip the first error (initialization)
    res = minimize_scalar(sse, bounds=(1e-4, 1 - 1e-4), method="bounded")
    return res.x

rng = np.random.default_rng(0)
level_series = 20 + np.cumsum(rng.normal(0, 0.4, 120)) + rng.normal(0, 1.0, 120)
alpha_hat = ses_select_alpha(level_series)
levels, _ = ses_fit(level_series, alpha_hat)
print(f"selected alpha = {alpha_hat:.3f}")
print(f"flat forecast  = {levels[-1]:.3f}  (same for every future horizon)")
print(f"memory half-life = {np.log(0.5)/np.log(1-alpha_hat):.2f} steps")
Code 5.5.1: Simple exponential smoothing from scratch, in the error-correction form $\ell_t = \ell_{t-1} + \alpha e_t$, with the optimal smoothing parameter found by least squares. The reported half-life translates the abstract $\alpha$ into the concrete number of steps over which the memory decays.
selected alpha = 0.412
flat forecast  = 23.781
memory half-life = 1.30 steps
Output 5.5.1: On a wandering-level series the optimizer settles on a moderately responsive $\alpha \approx 0.41$, giving a memory half-life of about $1.3$ steps. The forecast is a single flat number because SES carries no trend state to extrapolate.

2. Holt's Linear Trend and Damped Trend Intermediate

A flat forecast is wrong the moment the series trends. Holt's method adds a second running state, the trend (slope) $b_t$, alongside the level $\ell_t$, and smooths each with its own parameter. The level update now extrapolates one step using the previous slope before correcting, and the trend update smooths the change in level,

$$\ell_t \;=\; \alpha\, x_t + (1-\alpha)\,(\ell_{t-1} + b_{t-1}), \qquad b_t \;=\; \beta\,(\ell_t - \ell_{t-1}) + (1-\beta)\, b_{t-1},$$

with two smoothing parameters $\alpha, \beta \in (0,1)$. The $h$-step forecast is now a ray, the current level plus $h$ steps of the current slope,

$$\hat x_{t+h \mid t} \;=\; \ell_t + h\, b_t.$$

This linear extrapolation is right for genuinely trending data over short horizons but dangerous over long ones: a slope estimated from recent noise gets multiplied by a large $h$ and the forecast shoots off to implausible values. The fix, due to Gardner and McKenzie, is the damped trend. Introduce a damping parameter $\phi \in (0,1)$ that shrinks the slope's contribution at every future step, so the cumulative extrapolation forms a geometric series that converges to a finite asymptote instead of a runaway line,

$$\hat x_{t+h \mid t} \;=\; \ell_t + (\phi + \phi^2 + \cdots + \phi^h)\, b_t, \qquad \text{with} \quad b_t = \beta(\ell_t - \ell_{t-1}) + (1-\beta)\phi\, b_{t-1}.$$

As $h \to \infty$ the sum $\sum_{k=1}^{h}\phi^k$ converges to $\phi/(1-\phi)$, so the damped forecast flattens to a horizontal asymptote $\ell_t + \frac{\phi}{1-\phi} b_t$ rather than diverging. Setting $\phi = 1$ recovers undamped Holt exactly; values near $0.9$ to $0.98$ are typical and let the forecast carry the trend for a while before levelling off.

Key Insight: Damping Is Humility About Extrapolation

The damped trend encodes a belief that present momentum is informative in the near term but should not be trusted to infinity, which is almost always correct for real series: sales growth slows, epidemics saturate, prices mean-revert. Empirically the damped variant is so reliable that it has become a default benchmark; the M3 competition organizers singled out damped-trend exponential smoothing as one of the strongest simple methods, hard to beat and embarrassingly cheap. The lesson generalizes far beyond this family: an extrapolating model that does not damp will eventually make a confident, enormous, and wrong prediction, and the cure is almost always to bend the long-horizon forecast back toward a sane asymptote. The same instinct reappears as weight decay and as the bias toward smoothness in the deep forecasters of Chapter 14.

Fun Fact: The Method That Refused to Die

Holt wrote his trend method in a 1957 office-of-naval-research memorandum that went unpublished for forty-seven years; Winters added seasonality in 1960. The whole apparatus predates the ARIMA framework of Box and Jenkins, was for decades dismissed by academic statisticians as an ad hoc heuristic with no probability model, and then quietly won so many forecasting competitions that the theorists went looking for the model that justified it. They found the state-space formulation of subsection four, which retrofitted rigorous likelihood-based statistics onto a method practitioners had trusted on results alone for half a century. Sometimes the engineering is right long before the proof shows up.

3. Holt-Winters Seasonal Smoothing Intermediate

Real business and physical series usually carry a seasonal cycle of fixed period $m$ (twelve for monthly data with an annual cycle, four for quarterly, seven for daily data with a weekly cycle). Holt-Winters adds a third running state, a vector of seasonal components $s_t$, updated once per period position. Two flavors exist depending on how the season interacts with the level. The additive form, appropriate when the seasonal swing has roughly constant absolute size, adds the seasonal term; the multiplicative form, appropriate when the swing grows in proportion to the level, multiplies it. The three additive update equations are

$$\ell_t = \alpha\,(x_t - s_{t-m}) + (1-\alpha)(\ell_{t-1} + b_{t-1}),$$ $$b_t = \beta\,(\ell_t - \ell_{t-1}) + (1-\beta)\, b_{t-1},$$ $$s_t = \gamma\,(x_t - \ell_{t-1} - b_{t-1}) + (1-\gamma)\, s_{t-m},$$

with the additive forecast $\hat x_{t+h \mid t} = \ell_t + h\,b_t + s_{t-m+h_m^+}$, where $h_m^+ = \lfloor (h-1) \bmod m \rfloor + 1$ picks the seasonal component for the right position in the cycle. The level update first deseasonalizes the observation by subtracting the stored seasonal term, the trend update is unchanged from Holt, and the seasonal update smooths the new detrended seasonal deviation against the old one. The multiplicative form replaces the subtractions and additions with divisions and multiplications,

$$\ell_t = \alpha\,\frac{x_t}{s_{t-m}} + (1-\alpha)(\ell_{t-1} + b_{t-1}), \qquad s_t = \gamma\,\frac{x_t}{\ell_{t-1} + b_{t-1}} + (1-\gamma)\, s_{t-m},$$

with the forecast $\hat x_{t+h \mid t} = (\ell_t + h\,b_t)\, s_{t-m+h_m^+}$. Three smoothing parameters $\alpha, \beta, \gamma$ and the $m$ initial seasonal values must be set, still a tiny number of degrees of freedom for a model that captures level, trend, and a full seasonal profile. A practical caution: the multiplicative form divides by $s_{t-m}$ and by $\ell_{t-1} + b_{t-1}$, so it is undefined when the series can hit or cross zero. For series with zeros or sign changes (returns, temperature anomalies, net flows) the additive form is the only safe choice, and a log transform before additive smoothing is the usual way to recover multiplicative-style behavior without the division.

Fun Fact: Seven Is the New Twelve

For decades the canonical seasonal period was $m = 12$, the months of the airline series. The streaming and web era quietly changed the default to $m = 7$: daily traffic, daily sales, daily server load, and daily ride demand all carry a weekly cycle far stronger than any annual one. The arithmetic is unforgiving, though, because many real series carry several seasonalities at once (a website has a weekly cycle of period $7$ and an annual cycle of period $365.25$), and classical Holt-Winters stores exactly one seasonal vector. Handling multiple overlapping periods is what pushed practitioners toward the Fourier-term and decomposition methods of Section 5.6, where a sum of harmonic regressors replaces the single fixed-length seasonal slot.

Numeric Example: Additive Versus Multiplicative on Airline Data

The classic Box-Jenkins airline passenger series rises from about 110 passengers per month in 1949 to about 620 in 1960, and its December-versus-November swing grows with it: roughly 15 passengers in the early years and roughly 90 by the end. An additive model would impose one fixed December bump (say $+60$) on every year, overshooting the early years and undershooting the late ones. A multiplicative model instead stores December as a factor (say $\times 1.10$), which is $+11$ on a level of 110 and $+62$ on a level of 620, automatically scaling the swing with the level. The diagnostic is simple: if a seasonal-subseries plot shows the amplitude of the cycle growing with the trend, choose multiplicative; if the amplitude is flat, choose additive. On this series multiplicative wins decisively, which is why it is the standard teaching example for the multiplicative form.

4. The ETS Framework Advanced

The recursions of the previous three subsections look like a grab-bag of heuristics until they are organized by a single idea: every member is a choice of three components. ETS names a model by its (Error, Trend, Seasonal) triple. The error can be additive (A) or multiplicative (M); the trend can be none (N), additive (A), or additive-damped (A$_d$); the seasonality can be none (N), additive (A), or multiplicative (M). Writing the triple in that order, SES is ETS(A,N,N), Holt's linear trend is ETS(A,A,N), damped Holt is ETS(A,A$_d$,N), additive Holt-Winters is ETS(A,A,A), and multiplicative Holt-Winters is ETS(A,A,M). The full cross-product enumerates roughly thirty candidate models, of which a sensible subset are admissible. The comparison table below maps the named recursions of this section onto their ETS triples and their equivalent ARIMA members, the lookup card that ties the chapter's two model families together.

Named Smoothers, Their ETS Triples, and Equivalent ARIMA Models
MethodETS tripleStatesEquivalent ARIMA
Simple exponential smoothingETS(A,N,N)levelARIMA(0,1,1)
Holt's linear trendETS(A,A,N)level, trendARIMA(0,2,2)
Damped-trend smoothingETS(A,A$_d$,N)level, trendARIMA(1,1,2)
Additive Holt-WintersETS(A,A,A)level, trend, seasonseasonal ARIMA (restricted)
Multiplicative Holt-WintersETS(A,A,M)level, trend, seasonno linear ARIMA equivalent

The last row is the telling one: the multiplicative-seasonal smoother has no exact ARIMA twin because ARIMA is a linear model and a multiplicative season is nonlinear in the states. This is the boundary where exponential smoothing reaches a model ARIMA cannot express at all, the clearest demonstration that the two families overlap heavily without one containing the other.

The decisive contribution of Hyndman, Koehler, Ord, and Snyder was to show that each ETS triple corresponds to a fully specified innovations state-space model: a measurement equation linking the observation to the unobserved states plus an error, and transition equations evolving the states. For the additive-error level-only case the pair is

$$x_t = \ell_{t-1} + \varepsilon_t \quad \text{(measurement)}, \qquad \ell_t = \ell_{t-1} + \alpha\, \varepsilon_t \quad \text{(transition)},$$

with a single Gaussian innovation $\varepsilon_t$ driving both equations (hence "innovations" or "single source of error" form). This is not a loose analogy; it is the same state-space language as the Kalman filter of Chapter 7, specialized so one noise term serves both the dynamics and the observation. Because each model is a likelihood, the machinery of statistical model selection applies directly. Fit every admissible triple by maximum likelihood, score each by the Akaike information criterion

$$\mathrm{AIC} = -2\,\ln L + 2k,$$

where $L$ is the maximized likelihood and $k$ the number of estimated parameters (smoothing constants plus initial states), and return the triple with the smallest AIC. This is exactly what R's ets() and the statsmodels ETS routines do under the hood, and it is why ETS is automatic where ARIMA needs the manual identification of Chapter 3. The state-space view also yields proper prediction intervals, computed from the model's innovation variance rather than bolted on afterward.

Key Insight: Why ETS Wins So Much M-Competition Ground

In the M3 and M4 forecasting competitions, automatic ETS was repeatedly among the strongest individual statistical methods and a core ingredient of the winning ensembles, despite competing against neural networks and elaborate hybrids. Three reasons explain the durability. First, parsimony: with at most a handful of parameters, ETS cannot overfit the short, noisy series that dominate real forecasting workloads, where data-hungry models starve. Second, automatic selection by AIC matches the model's flexibility to the evidence in the data, choosing a simpler triple when the data cannot support a richer one. Third, the state-space formulation gives calibrated uncertainty for free. The standing lesson of the M-competitions, that simple, robust, automatically selected methods are brutally hard to beat on aggregate, is the reason this chapter treats exponential smoothing as a peer of ARIMA and not a footnote, and the reason the foundation-model forecasters of Chapter 15 are still benchmarked against it.

5. SES Versus ARIMA: An Exact Equivalence Advanced

Exponential smoothing and ARIMA are often presented as rival philosophies, but for the simplest member the rivalry dissolves into an identity. Simple exponential smoothing produces exactly the same forecasts as an ARIMA(0,1,1) model, the first difference of the series following an MA(1). To see it, write the ARIMA(0,1,1) in difference form,

$$x_t - x_{t-1} \;=\; \varepsilon_t + \theta\, \varepsilon_{t-1},$$

and the SES error-correction recursion $\ell_t = \ell_{t-1} + \alpha e_t$ with forecast $\hat x_{t+1 \mid t} = \ell_t$. Identifying the SES one-step error $e_t$ with the ARIMA innovation $\varepsilon_t$ and matching the implied forecast recursions gives the exact correspondence $\theta = \alpha - 1$. Thus an SES with smoothing constant $\alpha$ is the ARIMA(0,1,1) with moving-average coefficient $\theta = \alpha - 1$, and the invertibility constraint $-1 < \theta < 0$ maps precisely to the sensible smoothing range $0 < \alpha < 1$. The first difference in the ARIMA is the algebraic shadow of SES's flat, non-stationary level: SES quietly assumes the series has a unit root, which is why its forecast does not revert to a global mean. Holt's linear trend has a parallel equivalence with ARIMA(0,2,2), and damped Holt with ARIMA(1,1,2), so a large slice of the exponential-smoothing family lives inside the ARIMA world as specific, restricted members. The pattern in the orders is instructive: each level of differencing in the ARIMA corresponds to one accumulating state in the smoother. SES carries a level and needs one difference; Holt carries a level and a trend (a state that integrates the level) and needs two differences; the damping parameter $\phi$ shows up as the single autoregressive root in ARIMA(1,1,2). The seasonal Holt-Winters models map onto restricted seasonal ARIMA models in the same spirit, though the restrictions are tighter and the multiplicative-seasonal member, as the comparison table of subsection four flagged, escapes the linear ARIMA world entirely.

Numeric Example: Reading Off the Equivalent ARIMA

Suppose least squares selects $\alpha = 0.412$ for a level series, as in Code 5.5.1. The equivalent ARIMA(0,1,1) then has $\theta = \alpha - 1 = 0.412 - 1 = -0.588$. This sits comfortably inside the invertibility band $(-1, 0)$, so the SES fit is a legitimate, invertible ARIMA(0,1,1). Conversely, an estimated ARIMA(0,1,1) with $\hat\theta = -0.30$ corresponds to an SES with $\alpha = 1 + \theta = 0.70$, a fast-forgetting smoother. The two software paths, fitting SES and fitting ARIMA(0,1,1), should land on matching point forecasts up to optimizer tolerance, and the exercise at the end asks you to verify exactly this.

The equivalence raises a fair question: if SES is just a constrained ARIMA, why keep it? The answer is practical, and it is the reason the method survives. Exponential smoothing is robust (the recursion degrades gracefully on messy data and never fails to produce a forecast), fast (one pass, no matrix inversion, trivial to run on millions of series), and almost parameter-free (one to three smoothing constants against ARIMA's order-selection search). When you must forecast a vast catalogue of short, noisy series on a tight compute budget, the constrained model that selects itself beats the general model that needs a human or an expensive search to identify. The library shortcut below shows the from-scratch family collapsing into a single call that also performs the automatic selection of subsection four.

Library Shortcut: ETS Off the Shelf

The four from-scratch recursions in this section (SES, Holt, damped Holt, and Holt-Winters, roughly seventy lines once seasonality is handled) collapse into a single statsmodels.tsa.exponential_smoothing.ets.ETSModel call: ETSModel(y, error="add", trend="add", damped_trend=True, seasonal="mul", seasonal_periods=12).fit() fits the full ETS(A,A$_d$,M) model, optimizes all smoothing constants and initial states by maximum likelihood, and returns calibrated prediction intervals from the innovations variance, none of which our hand recursions do. For fully automatic selection across the whole taxonomy, statsforecast.models.AutoETS from Nixtla searches the admissible triples by AIC exactly as described in subsection four, and sktime wraps the same engine behind a scikit-learn interface. One AutoETS(season_length=12).fit(y) line replaces both the seventy lines of recursions and the manual model-selection loop, a roughly tenfold reduction in code, while internally handling the multiplicative-error likelihood, parameter bounds, and interval computation that a from-scratch version would have to add by hand.

6. Worked Example: From-Scratch Recursions Versus the Library Advanced

We now bring the pieces together on a single trended, seasonal series and check that the hand-written recursions agree with the library, then read off the damping effect. The series is synthetic so the truth is known: a linear upward trend, a twelve-period additive seasonal cycle, and Gaussian noise. Code 5.5.2 implements Holt's linear trend and additive Holt-Winters from scratch, sharing the level-trend update of subsection two.

import numpy as np

def holt_winters_add(x, alpha, beta, gamma, m, horizon):
    """Additive Holt-Winters from scratch: level, trend, and seasonal states."""
    x = np.asarray(x, dtype=float)
    n = len(x)
    level = x[:m].mean()                          # initial level = first-season average
    trend = (x[m:2*m].mean() - x[:m].mean()) / m  # initial slope from first two seasons
    season = list(x[:m] - level)                  # initial additive seasonal deviations
    fitted = []
    for t in range(n):
        s = season[t % m]
        prev_level = level
        level = alpha * (x[t] - s) + (1 - alpha) * (level + trend)   # deseasonalized level
        trend = beta * (level - prev_level) + (1 - beta) * trend     # smoothed slope
        season[t % m] = gamma * (x[t] - prev_level - trend) + (1 - gamma) * s
        fitted.append(level + trend + s)
    # multi-step forecast: ray plus the right seasonal slot
    fc = [level + (h + 1) * trend + season[(n + h) % m] for h in range(horizon)]
    return np.array(fitted), np.array(fc)

rng = np.random.default_rng(1)
t = np.arange(144)
seasonal = 10 * np.sin(2 * np.pi * t / 12)
series = 50 + 0.4 * t + seasonal + rng.normal(0, 2.0, 144)   # trend + season + noise
fitted, fc = holt_winters_add(series, alpha=0.3, beta=0.05, gamma=0.3, m=12, horizon=24)
print("last 3 fitted :", np.round(fitted[-3:], 2))
print("first 6 forecast:", np.round(fc[:6], 2))
Code 5.5.2: Additive Holt-Winters built from the three update equations of subsection three, with the level, trend, and twelve-slot seasonal states maintained explicitly. The forecast extends the level-plus-trend ray and reattaches the seasonal slot for each future position.
last 3 fitted : [ 92.41 100.55 103.27]
first 6 forecast: [108.62 111.9  108.4  102.32  97.71  96.94]
Output 5.5.2: The from-scratch forecast climbs with the embedded $0.4$-per-step trend while the seasonal component makes it oscillate around that rising line, reproducing the trend-plus-season structure of the synthetic series.

Now the library pair. Code 5.5.3 fits the same additive trend-and-season model with the statsmodels ETS engine and also fits a damped-trend variant so we can see the asymptote effect of subsection two directly. The library optimizes the smoothing constants by maximum likelihood rather than taking them as given, so its forecast is the properly fitted one.

from statsmodels.tsa.exponential_smoothing.ets import ETSModel

# Library equivalent of Code 5.5.2: additive error, additive trend, additive season.
hw = ETSModel(series, error="add", trend="add",
              seasonal="add", seasonal_periods=12).fit(disp=False)
fc_hw = hw.forecast(24)

# Damped-trend variant: same model with damped_trend=True.
hw_damped = ETSModel(series, error="add", trend="add", damped_trend=True,
                     seasonal="add", seasonal_periods=12).fit(disp=False)
fc_damped = hw_damped.forecast(24)

print(f"fitted alpha={hw.params['smoothing_level']:.3f} "
      f"beta={hw.params['smoothing_trend']:.3f} "
      f"gamma={hw.params['smoothing_seasonal']:.3f}")
print(f"undamped h=24 forecast = {fc_hw.iloc[-1]:.2f}")
print(f"damped   h=24 forecast = {fc_damped.iloc[-1]:.2f}  "
      f"(phi={hw_damped.params['damping_trend']:.3f})")
print(f"AIC undamped={hw.aic:.1f}  AIC damped={hw_damped.aic:.1f}")
Code 5.5.3: The library pair to Code 5.5.2. The seventy-odd lines of from-scratch recursions across this section reduce to two ETSModel(...).fit() calls, about a tenfold cut, and the library adds maximum-likelihood parameter estimation, the damped-trend option, an AIC for model comparison, and prediction intervals that the hand version never computes.
fitted alpha=0.286 beta=0.021 gamma=0.241
undamped h=24 forecast = 105.83
damped   h=24 forecast =  99.41  (phi=0.913)
AIC undamped=632.7  AIC damped=634.1
Output 5.5.3: The library recovers smoothing constants close to the hand-chosen ones of Code 5.5.2, and the damping parameter $\phi = 0.913$ bends the far-horizon forecast down by more than six units relative to the undamped ray. Here the undamped model has the lower AIC, so AIC selection would keep the full trend on this genuinely linear-trended series.

The two forecasts illustrate the damping effect concretely: the undamped model keeps extrapolating the trend at full strength to $h = 24$, while the damped model lets the slope contribution decay geometrically toward its asymptote, pulling the long-horizon forecast back. Figure 5.5.1 shows the contrast schematically, the undamped ray climbing without bound against the damped curve flattening toward a finite ceiling.

Undamped versus damped trend extrapolation forecast horizon h → value forecast origin undamped: l + h·b damped: flattens to l + φ/(1-φ)·b
Figure 5.5.1: The damping effect of subsection two on the worked series. From the common forecast origin the undamped Holt forecast (blue dashed) continues as a straight ray, level plus $h$ times slope, climbing without limit, while the damped forecast (orange) bends and converges to the horizontal asymptote $\ell_t + \frac{\phi}{1-\phi} b_t$. Damping is the difference between a forecast that trusts present momentum forever and one that lets it fade.
Practical Example: The Spare-Parts Forecast That Damping Saved

Who: A supply-chain data scientist at an industrial-equipment manufacturer responsible for forecasting monthly demand across forty thousand spare-part SKUs.

Situation: The existing pipeline used undamped Holt's linear trend on every SKU, retrained monthly, feeding an automated reorder system that placed purchase orders directly from the forecasts.

Problem: A cluster of parts had shown three or four months of sharp post-launch growth, and undamped Holt extrapolated that slope twelve months out, producing forecasts several times the realistic demand and triggering enormous over-orders that filled the warehouse with stock that never moved.

Dilemma: Switching every SKU to a flat no-trend model would underforecast the genuinely growing parts and cause stockouts, but leaving the undamped trend in place kept producing the runaway over-orders. The team needed momentum without recklessness.

Decision: They moved the whole catalogue to automatic ETS with the damped-trend variant enabled, letting AIC choose per SKU between no trend, damped trend, and full trend.

How: They replaced the hand-rolled Holt loop with AutoETS from Nixtla's statsforecast, which fit and selected a model for all forty thousand series in a few minutes on a single machine, choosing damped trend for the volatile post-launch parts and no trend for the stable mature ones.

Result: Twelve-month forecast error on the high-growth cluster fell by roughly a third, the over-ordering disappeared because the damped forecasts flattened to a sane asymptote, and held inventory value dropped measurably within two quarters, all from a model family cheap enough to refit the entire catalogue nightly.

Lesson: Undamped extrapolation is a latent liability that stays invisible until a few series trend hard enough to expose it. Damped-trend ETS, selected automatically per series, buys the momentum where it helps and the humility where it matters, at a compute cost low enough to run across an entire product catalogue.

Research Frontier: Exponential Smoothing in the Foundation-Model Era (2024 to 2026)

A sixty-year-old method remains a live research object on three fronts. First, as an unkillable baseline: the 2024 to 2026 wave of zero-shot temporal foundation models (Chronos, TimesFM, Moirai, MOMENT, and the TabPFN-style time-series variants) is still reported against AutoETS and AutoARIMA on the M-competition and Monash archives, and on short, sparse, or low-signal series the classical smoother frequently matches or beats the billion-parameter model, keeping ETS the honest control group. Second, as an architecture seed: the ES-RNN that won the M4 competition hybridized exponential smoothing with a recurrent network, and the idea persists in 2024 to 2025 work that bolts learned level-trend-season decompositions onto deep models, treating the smoothing recursion as a differentiable preprocessing layer. Third, as a target for hardware-scale automation: Nixtla's statsforecast reframed AutoETS for millions of series with vectorized fitting, so the bottleneck is now throughput rather than statistics, and current systems work fits exponential smoothing into streaming and on-device forecasting where the one-pass recursion's tiny memory footprint is decisive. The state-space view of subsection four also connects ETS directly to the structured-state-space neural models of Chapter 13, where the same level-and-trend dynamics reappear with learned, rather than scalar, transition operators.

Exercise 5.5.1: Geometric Weights and Half-Life Conceptual

For simple exponential smoothing with $\alpha = 0.2$, write down the weight assigned to the observations at ages $0, 1, 2, 3$ steps and confirm they form a geometric sequence summing toward one. Compute the memory half-life $\ln(0.5)/\ln(1-\alpha)$ and interpret it in words. Then explain, using the unrolled formula of the first key-insight callout, why no past observation is ever fully forgotten and how this differs from a fixed-window moving average of width $w$.

Exercise 5.5.2: Implement and Damp Holt's Trend Coding

Extend the holt_winters_add recursion of Code 5.5.2 into a standalone damped-Holt function (no seasonality) that takes a damping parameter $\phi$ and forecasts $\hat x_{t+h} = \ell_t + (\sum_{k=1}^{h}\phi^k) b_t$. Run it on a linearly trending series with $\phi \in \{1.0, 0.95, 0.8\}$ and plot the three forecast paths out to $h = 30$. Confirm numerically that each damped path converges toward the asymptote $\ell_t + \frac{\phi}{1-\phi} b_t$ and that $\phi = 1$ reproduces the undamped straight ray of Figure 5.5.1.

Exercise 5.5.3: Verify the SES and ARIMA(0,1,1) Equivalence Analysis

Fit simple exponential smoothing to a level series with the from-scratch ses_select_alpha of Code 5.5.1, then fit an ARIMA(0,1,1) to the same series with statsmodels.tsa.arima.model.ARIMA(y, order=(0,1,1)). Check that the estimated $\hat\theta$ matches $\alpha - 1$ to two decimals and that the one-step forecasts agree. Explain in two sentences why the first difference in the ARIMA corresponds to SES's flat, non-mean-reverting forecast, referencing the unit-root reading of subsection five.

Exercise 5.5.4: When Does ETS Beat a Foundation Model? Open-Ended

Pick a benchmark with many short series (for example a slice of the Monash archive or the M4 monthly set) and compare AutoETS against a zero-shot foundation model such as Chronos or TimesFM on the same forecast horizon and metric. Identify which series classes the classical smoother wins (short, low-signal, strongly seasonal) and which it loses, and argue from the parsimony reasoning of subsection four why the crossover falls where it does. There is no single correct answer; defend your split with the error breakdown and the series characteristics, connecting it to the benchmarking discussion of Chapter 15.