Type at least 2 letters. Enter opens the first result, Esc closes.

    Time Series and Forecasting · Lesson 1 · Time series data

    Time series data: trend, seasonality, noise and decomposition

    Beginner 60 min 4 interactive figures 4 videos Colab notebook

    Before this lesson: Python and NumPy arrays (foundations track); mean and variance (probability-and-statistics track). Nothing else. Time: about 60 minutes, plus the notebook.


    Why this matters #

    Forecasting is everywhere: product demand, electricity load, weather, traffic, server metrics, hospital admissions. Each of these is a time series, and the first step is always to look at the data and find its trend and its seasons.

    Ordinary machine learning assumes that examples are i.i.d. (independent and identically distributed). Time series break both parts. Neighbouring values are dependent, and the distribution can change over time (a trend, a new regime). So a random train/test split puts future values into the training set. Lesson 03 shows how to evaluate honestly with a split in time order.

    Decomposition is used directly in practice and in models:

    • Official statistics publish seasonally adjusted unemployment and retail figures, made with X-13ARIMA-SEATS or similar decomposition methods.
    • Forecasting models are built from the same parts. Prophet (Taylor and Letham, 2018) is an additive model of trend, seasonality and holiday effects. The deep models Autoformer (Wu et al., 2021) and DLinear (Zeng et al., 2023) split their input into a moving-average trend and a remainder before the main layers (lesson 09).
    • Features and monitoring. Trend and seasonal strength are features for ML models (lesson 08), and large remainders flag anomalies (lesson 11).

    1. Predict first #

    2. The picture #

    What a time series is #

    A time series is a sequence of values y1,y2,…,yny_1, y_2, \dots, y_n observed in time order. The index tt is time and nn is the length. Most series in this track are regularly sampled: one value every hour, day, week, month or quarter. This spacing is the frequency of the series.

    Three plots to make first #

    The time plot shows yty_t against tt. Look for a long-term rise or fall (trend), a pattern that repeats (seasonality), sudden jumps, and single unusual values.

    The seasonal plot draws one line per year (or per week) against the position inside the period: month 1 to 12, or Monday to Sunday. When the lines have the same shape, the series has a stable seasonal pattern. When the lines are stacked upward, there is also a trend.

    The lag plot is a scatter plot of yty_t against yt−ky_{t-k}, the value kk steps earlier. The number kk is the lag. If the points lie close to a rising line, values kk steps apart are similar. A seasonal series with period 12 lines up well at k=12k = 12, because January is compared with January.

    Interactive figure: loading…

    Move the lag kk and watch the right panel. Each blue dot is one pair (yt−k,yt)(y_{t-k}, y_t); the yellow pair is marked in both panels. For "season + noise" the dots line up at k=12k = 12 (and k=24k = 24), and they form a falling cloud at k=6k = 6, because a seasonal high is followed by a low half a year later. Switch to "trend + season": the trend makes every lag look strongly correlated. The readout r(k)r(k) is the correlation of the pairs; lesson 02 turns it into the autocorrelation function.

    Trend, season and remainder #

    Most series are a combination of three components:

    • the trend-cycle TtT_t: the slow, long-term movement of the level (it includes slow cycles, see below);
    • the seasonal component StS_t: a pattern that repeats with a fixed, known period mm, for example m=12m = 12 for monthly data with a yearly pattern, m=7m = 7 for daily data with a weekly pattern, m=4m = 4 for quarterly data;
    • the remainder RtR_t: everything else, that is noise and unusual events.

    The simplest combination is a sum: yt=Tt+St+Rty_t = T_t + S_t + R_t. The figure builds a series from these three parts. Then it takes the series apart again with the method of section 3 and draws the recovered parts as dashed lines.

    Interactive figure: loading…

    Build a series from its parts, then take it apart. The top panel is the observed series yty_t. Below are the true trend, seasonal and remainder parts (solid) and the parts recovered by classical decomposition (dashed). Raise the noise σ\sigma or shorten the series to n=24n = 24: the recovered seasonal pattern becomes wrong, because each seasonal index is then an average of only one or two noisy values. Note that the recovered trend has no values at both ends. Switch to "multiplicative": the seasonal swing grows with the level.

    Quick check

    Monthly ice-cream sales (no trend) are high every summer and low every winter. In the lag plot of yty_t against yt−6y_{t-6}, where do the points lie?

    3. Definitions and main results #

    3.1 Time series data in practice #

    • A series is univariate when one quantity is measured, and multivariate when several quantities are measured at the same times (temperature, pressure and wind at one station).
    • Often there are many related series, for example the sales of 10 000 products. This is called panel data. One model can be trained on all of them together: a global model (lesson 08).
    • Exogenous variables (also called covariates) are other inputs that help to forecast yty_t. Some are known in advance: the calendar, public holidays, planned promotions. Others are only observed: the weather. To use an observed-only variable for forecasting, you need a forecast of it too.

    In pandas, a time series is a Series with a DatetimeIndex. Four tools cover most data cleaning:

    • pd.date_range("2024-01-01", periods=36, freq="MS") makes a regular index (here: month starts).
    • s.resample("MS").mean() changes the frequency, for example from weekly to monthly means. Note that mean() skips missing values: a month with one valid week still gets a value.
    • s.asfreq("D") puts the series on a regular daily grid. A missing time stamp (a row that does not exist) becomes a visible missing value (NaN). These are different problems: many methods silently assume that consecutive rows are one step apart.
    • s.tz_localize("Europe/Zurich").tz_convert("UTC") handles time zones. With daylight saving time, the day when the clocks change has 23 hours (spring) or 25 hours (autumn). Store time stamps in UTC and convert only for display.

    3.2 Components and the two decomposition models #

    Seasonal or cyclic? A seasonal pattern has a fixed, known period that comes from the calendar or the clock (year, week, day). A cycle is a rise and fall without a fixed period, for example business cycles of a few years. The sunspot cycle is about 11 years long, but its length varies (roughly 9 to 14 years), so it is cyclic, not seasonal. Cycles cannot be separated reliably from the trend over a few years of data, so decomposition puts them into the trend-cycle TtT_t.

    Definition (decomposition models).

    additive: yt=Tt+St+Rt,multiplicative: yt=Tt⋅St⋅Rt.\text{additive: } y_t = T_t + S_t + R_t, \qquad \text{multiplicative: } y_t = T_t \cdot S_t \cdot R_t .

    In the additive model the seasonal swing has the same size at every level. In the multiplicative model StS_t and RtR_t are factors near 1: a seasonal factor of 1.21.2 means "20 % above the trend", so the swing in units grows with the level.

    The log transform turns multiplicative into additive. Take logs of both sides:

    log⁡yt=log⁡Tt+log⁡St+log⁡Rt.\log y_t = \log T_t + \log S_t + \log R_t .

    This is an additive model for log⁡yt\log y_t. So the standard recipe is: if the swing grows with the level, take logs, decompose additively, and transform back with exp⁡\exp.

    The Box–Cox transform is a family that contains the log as a special case. For yt>0y_t > 0 and a parameter λ\lambda,

    wt={(ytλ−1)/λ,λ≠0,log⁡yt,λ=0.w_t = \begin{cases} (y_t^\lambda - 1)/\lambda, & \lambda \ne 0, \\ \log y_t, & \lambda = 0. \end{cases}

    λ=1\lambda = 1 only shifts the series by −1-1; λ=1/2\lambda = 1/2 gives 2yt−22\sqrt{y_t} - 2, a square root up to a shift and a scale. The case λ=0\lambda = 0 is the limit of the first line as λ→0\lambda \to 0, because yλ=eλlog⁡y≈1+λlog⁡yy^\lambda = e^{\lambda \log y} \approx 1 + \lambda \log y for small λ\lambda. A value between 0 and 1 helps when the swing grows with the level, but more slowly than in proportion.

    Interactive figure: loading…

    Move λ\lambda from 1 to 0. The top panel is the transformed series wtw_t (blue). The bars show the seasonal swing (maximum minus minimum) inside each year (violet). On the original scale (λ=1\lambda = 1) the last swing is about 9 times the first one. At λ=0\lambda = 0 (log) the bars are almost equal: the multiplicative pattern has become additive.

    Quick check

    A shop's monthly sales swing by about 20 % above and below the trend, at every level. Which decomposition should you use?

    3.3 Moving averages #

    A moving average replaces each value by the mean of the values around it. Noise averages out, and a seasonal pattern averages out when the window covers exactly one period.

    Definition. For odd k=2h+1k = 2h + 1, the centred moving average of order kk (kk-MA) is

    T^t=1k∑j=−hhyt+j.\hat T_t = \frac{1}{k} \sum_{j=-h}^{h} y_{t+j} .

    The trailing moving average of order kk uses only the past: 1k∑j=0k−1yt−j\frac1k \sum_{j=0}^{k-1} y_{t-j}.

    Result 1 (a trailing MA lags behind a trend). For a straight line yt=a+bty_t = a + bt the trailing MA of order kk equals the line at time t−(k−1)/2t - (k-1)/2. So it is late by (k−1)/2(k-1)/2 steps.

    Proof.

    1. 1k∑j=0k−1(a+b(t−j))=a+bt−bk∑j=0k−1j\frac1k \sum_{j=0}^{k-1} \bigl(a + b(t - j)\bigr) = a + bt - \frac{b}{k} \sum_{j=0}^{k-1} j.
    2. ∑j=0k−1j=k(k−1)/2\sum_{j=0}^{k-1} j = k(k-1)/2, so the result is a+b(t−k−12)a + b\bigl(t - \tfrac{k-1}{2}\bigr). ■\blacksquare

    A centred MA has symmetric weights that sum to 1. By the same computation with jj from −h-h to hh, the sum ∑j\sum j is 0, so a centred MA returns a straight line unchanged: no lag.

    Result 2 (one full period removes the seasonal pattern). Let StS_t repeat with period mm (St+m=StS_{t+m} = S_t) and sum to 0 over one period. Then the average of any mm consecutive values of StS_t is 0.

    Proof. Any mm consecutive values St,…,St+m−1S_{t}, \dots, S_{t+m-1} contain each of the mm positions of the period exactly once, so their sum is the sum over one period, which is 0. ■\blacksquare

    The problem with even periods. For monthly data, m=12m = 12 is even. A window of 12 values, say months t−5,…,t+6t-5, \dots, t+6, has its centre at t+12t + \tfrac12, between two months. The fix is to average two neighbouring 12-MAs: one centred at t−12t - \tfrac12 and one at t+12t + \tfrac12. The result is centred at tt. It is called the 2×122 \times 12 MA.

    Result 3 (weights of the 2×m2 \times m MA). For even mm,

    T^t=12m yt−m/2+1m∑j=−m/2+1m/2−1yt+j+12m yt+m/2.\hat T_t = \frac{1}{2m}\, y_{t-m/2} + \frac{1}{m} \sum_{j=-m/2+1}^{m/2-1} y_{t+j} + \frac{1}{2m}\, y_{t+m/2} .

    For m=12m = 12: the weights are 124,112,…,112,124\tfrac1{24}, \tfrac1{12}, \dots, \tfrac1{12}, \tfrac1{24} on 13 values.

    Proof.

    1. The first 12-MA covers t−6,…,t+5t-6, \dots, t+5, the second covers t−5,…,t+6t-5, \dots, t+6. Each has weight 112\frac1{12} on each of its values.
    2. Their average gives weight 12⋅112=124\frac12 \cdot \frac1{12} = \frac1{24} to yt−6y_{t-6} and to yt+6y_{t+6} (each is in one window only), and 12⋅212=112\frac12 \cdot \frac2{12} = \frac1{12} to the 11 values in between (they are in both windows). The same argument works for any even mm. ■\blacksquare

    Months t−6t-6 and t+6t+6 are the same calendar month, so each calendar month gets total weight 112\frac1{12}. Therefore the 2×122 \times 12 MA also removes a period-12 pattern exactly (Result 2), and its weights are symmetric, so it keeps a straight-line trend.

    The end effect. A centred MA of order k=2h+1k = 2h + 1, or a 2×m2 \times m MA with h=m/2h = m/2, has no value at the first hh and the last hh time steps: the window does not fit. This is a serious limit for forecasting, because the newest values are the most important ones.

    Interactive figure: loading…

    Change the window kk and the type. The aqua line is the moving average of the data; the dashed line is the true trend. The yellow band shows the window used at one time, and the lower panel shows its weights. With "centred" and an even kk, the widget uses the 2×k2 \times k MA (note the half weights at both ends). At k=12k = 12 the leftover seasonal amplitude is 0. With "trailing" and k=12k = 12 the season is also removed, but the line is 5.5 steps late.

    Quick check

    You have daily data with a weekly pattern. Which moving average removes the weekly pattern and is centred?

    Moving averages as filters

    A moving average is a linear filter T^t=∑jwjyt+j\hat T_t = \sum_j w_j y_{t+j}. Feed it a pure wave yt=e2πifty_t = e^{2\pi i f t} and the output is the same wave times the number H(f)=∑jwje2πifjH(f) = \sum_j w_j e^{2\pi i f j}, the frequency response. For the mm-MA, H(f)=1m∑je2πifjH(f) = \frac1m \sum_{j} e^{2\pi i f j} is a geometric sum that is exactly 0 at f=1/m,2/m,…f = 1/m, 2/m, \dots. These are the frequencies of a period-mm pattern and of all its harmonics. This is the frequency view of Result 2. The 2×m2 \times m MA is the mm-MA applied after a 2-MA, so its response is a product of the two responses and also has these zeros. Lesson 02 meets frequencies again in the periodogram.

    3.4 Classical decomposition #

    Algorithm (classical additive decomposition) with seasonal period mm:

    1. Trend. T^t\hat T_t = the 2×m2 \times m MA if mm is even, the centred mm-MA if mm is odd.
    2. Detrend. dt=yt−T^td_t = y_t - \hat T_t wherever T^t\hat T_t exists.
    3. Seasonal indices. For each season j=1,…,mj = 1, \dots, m, average dtd_t over all times tt in season jj. Then subtract the mean of the mm averages, so the indices sum to 0. The seasonal component S^t\hat S_t repeats these mm indices.
    4. Remainder. R^t=yt−T^t−S^t\hat R_t = y_t - \hat T_t - \hat S_t.

    For the multiplicative version, divide instead of subtract: dt=yt/T^td_t = y_t / \hat T_t, scale the indices so they average to 1, and R^t=yt/(T^tS^t)\hat R_t = y_t / (\hat T_t \hat S_t). The seasonally adjusted series is yt−S^ty_t - \hat S_t (or yt/S^ty_t / \hat S_t): the data with the season removed.

    Weaknesses (FPP3 §3.4):

    • no trend (and no remainder) at the first and last ⌊m/2⌋\lfloor m/2 \rfloor time steps;
    • the moving average smooths over fast rises and falls of the trend;
    • the seasonal pattern is the same in every year, so a slowly changing season is not captured;
    • unusual values (outliers) enter the averages and spoil the trend and the seasonal indices.

    3.5 STL and other modern methods #

    STL (Seasonal and Trend decomposition using Loess; Cleveland et al., 1990) fixes most of these weaknesses. Loess is local regression: at each time it fits a low-degree polynomial to the nearby points, with more weight on the closest ones. STL repeats two smoothing steps:

    1. remove the current trend; for each season (all Januaries, all Februaries, …), smooth its values over the years with loess. This gives a seasonal pattern that may change slowly over time;
    2. remove that seasonal pattern and smooth the rest with loess to get the trend.

    An outer loop computes robustness weights: points with large remainders get small weights, so outliers do not spoil the trend and the season. Its main parameters are the seasonal window (how many years each seasonal smoothing uses: larger means a more stable pattern) and the trend window (larger means a smoother trend). STL works for any period, gives values at the ends, and is the usual default. It is additive, so use it on log⁡yt\log y_t for multiplicative series. National statistics offices use X-13ARIMA-SEATS (US Census Bureau) and SEATS, which are built for monthly and quarterly data with calendar effects.

    Strength of trend and seasonality (FPP3 §4.3). With the remainder RtR_t of a decomposition:

    FT=max⁡(0,  1−Var⁡(Rt)Var⁡(Tt+Rt)),FS=max⁡(0,  1−Var⁡(Rt)Var⁡(St+Rt)).F_T = \max\Bigl(0,\; 1 - \frac{\operatorname{Var}(R_t)}{\operatorname{Var}(T_t + R_t)}\Bigr), \qquad F_S = \max\Bigl(0,\; 1 - \frac{\operatorname{Var}(R_t)}{\operatorname{Var}(S_t + R_t)}\Bigr).

    Both lie in [0,1][0, 1]. If the seasonal part is large compared with the remainder, St+RtS_t + R_t varies much more than RtR_t and FSF_S is close to 1. With no seasonality, Var⁡(St+Rt)≈Var⁡(Rt)\operatorname{Var}(S_t + R_t) \approx \operatorname{Var}(R_t) and FSF_S is near 0.

    3.6 What decomposition is for #

    Decomposition describes the past. It does not forecast by itself: the trend from a centred MA stops ⌊m/2⌋\lfloor m/2 \rfloor steps before the end. To forecast with a decomposition (FPP3 §5.7), forecast the seasonally adjusted series with a non-seasonal method, forecast the seasonal part by repeating the last year, and add the two (lessons 03 and 04). Other uses: seasonal adjustment for reports, features for ML models, and residual-based anomaly detection.

    Note that the "trend" is not a fixed property of the data. It depends on the method and on the window: a longer window gives a smoother trend and pushes more movement into the remainder.

    Answers to section 1. (1) The yearly pattern is removed exactly (Result 2). But the plain 12-MA is centred between two months; the 2×122 \times 12 MA fixes this. (2) Multiplied: the swing doubles when the level doubles, so the model is multiplicative; the log makes it additive. (3) It is late by (5−1)/2=2(5-1)/2 = 2 steps, so it is 2⋅2=42 \cdot 2 = 4 units below the line (Result 1).

    Quick check

    A trailing 5-MA is applied to a series with a straight-line trend. By how many time steps is the smoothed trend late?

    4. Worked example #

    Quarterly data (m=4m = 4) for three years, t=1,…,12t = 1, \dots, 12:

    tt 1 2 3 4 5 6 7 8 9 10 11 12
    quarter Q1 Q2 Q3 Q4 Q1 Q2 Q3 Q4 Q1 Q2 Q3 Q4
    yty_t 14 9 6 15 16 11 8 17 19 12 10 21

    The swing has about the same size every year, so we use the additive model.

    Step 1: trend with the 2×42 \times 4 MA. The weights are 18,14,14,14,18\tfrac18, \tfrac14, \tfrac14, \tfrac14, \tfrac18 on yt−2,…,yt+2y_{t-2}, \dots, y_{t+2}. For t=3t = 3:

    T^3=18⋅14+14(9+6+15)+18⋅16=1.75+7.5+2=11.25.\hat T_3 = \tfrac18 \cdot 14 + \tfrac14 (9 + 6 + 15) + \tfrac18 \cdot 16 = 1.75 + 7.5 + 2 = 11.25 .

    For t=4t = 4: 18⋅9+14(6+15+16)+18⋅11=1.125+9.25+1.375=11.75\tfrac18 \cdot 9 + \tfrac14(6 + 15 + 16) + \tfrac18 \cdot 11 = 1.125 + 9.25 + 1.375 = 11.75. In the same way:

    tt 3 4 5 6 7 8 9 10
    T^t\hat T_t 11.25 11.75 12.25 12.75 13.375 13.875 14.25 15
    dt=yt−T^td_t = y_t - \hat T_t −5.25-5.25 3.25 3.75 −1.75-1.75 −5.375-5.375 3.125 4.75 −3-3

    There is no trend at t=1,2,11,12t = 1, 2, 11, 12 (the end effect, m/2=2m/2 = 2 at each end).

    Step 2: seasonal indices. Average the detrended values of each quarter:

    • Q1 (t=5,9t = 5, 9): (3.75+4.75)/2=4.25(3.75 + 4.75)/2 = 4.25
    • Q2 (t=6,10t = 6, 10): (−1.75−3)/2=−2.375(-1.75 - 3)/2 = -2.375
    • Q3 (t=3,7t = 3, 7): (−5.25−5.375)/2=−5.3125(-5.25 - 5.375)/2 = -5.3125
    • Q4 (t=4,8t = 4, 8): (3.25+3.125)/2=3.1875(3.25 + 3.125)/2 = 3.1875

    Their sum is −0.25-0.25, so their mean is −0.0625-0.0625. Subtract it from each:

    S^Q1=4.3125,S^Q2=−2.3125,S^Q3=−5.25,S^Q4=3.25,\hat S_{Q1} = 4.3125, \quad \hat S_{Q2} = -2.3125, \quad \hat S_{Q3} = -5.25, \quad \hat S_{Q4} = 3.25,

    and now the four indices sum to 0. Q4 is 3.25 above the trend on average, Q3 is 5.25 below it.

    Step 3: remainder. R^t=dt−S^t\hat R_t = d_t - \hat S_t:

    tt 3 4 5 6 7 8 9 10
    R^t\hat R_t 0 0 −0.5625-0.5625 0.5625 −0.125-0.125 −0.125-0.125 0.4375 −0.6875-0.6875

    For example R^5=3.75−4.3125=−0.5625\hat R_5 = 3.75 - 4.3125 = -0.5625. The remainders are small compared with the seasonal indices.

    Step 4: strength. On t=3,…,10t = 3, \dots, 10: Var⁡(R^t)=0.162\operatorname{Var}(\hat R_t) = 0.162, Var⁡(S^t+R^t)=15.68\operatorname{Var}(\hat S_t + \hat R_t) = 15.68 and Var⁡(T^t+R^t)=1.455\operatorname{Var}(\hat T_t + \hat R_t) = 1.455. So FS=1−0.162/15.68=0.99F_S = 1 - 0.162/15.68 = 0.99 (very strong seasonality) and FT=1−0.162/1.455=0.89F_T = 1 - 0.162/1.455 = 0.89 (a clear trend).

    Step 5: seasonally adjusted series. yt−S^ty_t - \hat S_t, for example y12−S^Q4=21−3.25=17.75y_{12} - \hat S_{Q4} = 21 - 3.25 = 17.75. Note that the seasonally adjusted series exists at the ends too, because the seasonal indices are known for every quarter.

    5. In code #

    NumPy reproduces every number of the worked example; the last lines show resample on a weekly series with gaps.

    import numpy as np
    import pandas as pd
    
    y = np.array([14, 9, 6, 15, 16, 11, 8, 17, 19, 12, 10, 21.])   # 3 years of quarters
    m = 4
    
    # 2x4 moving average: weights 1/8, 1/4, 1/4, 1/4, 1/8
    w = np.r_[0.5, np.ones(m - 1), 0.5] / m
    trend = np.full(len(y), np.nan)
    trend[m // 2 : -(m // 2)] = np.convolve(y, w, mode="valid")
    print(trend[2:10])        # [11.25  11.75  12.25  12.75  13.375 13.875 14.25  15.   ]
    
    detr = y - trend
    raw = np.array([np.nanmean(detr[j::m]) for j in range(m)])
    idx = raw - raw.mean()    # centred seasonal indices
    print(raw, idx)           # [ 4.25 -2.375 -5.3125 3.1875] [ 4.3125 -2.3125 -5.25 3.25 ]
    
    seasonal = np.tile(idx, len(y) // m)
    remainder = y - trend - seasonal
    print(remainder[2:10])    # [ 0.  0. -0.5625  0.5625 -0.125 -0.125  0.4375 -0.6875]
    
    ok = ~np.isnan(trend)
    F_T = max(0, 1 - remainder[ok].var() / (trend[ok] + remainder[ok]).var())
    F_S = max(0, 1 - remainder[ok].var() / (seasonal[ok] + remainder[ok]).var())
    print(round(F_T, 2), round(F_S, 2))   # 0.89 0.99
    
    # pandas: a weekly series with gaps -> monthly means (NaN weeks are skipped)
    idx_w = pd.date_range("2024-01-06", periods=10, freq="W-SAT")
    s = pd.Series([5., 6, np.nan, 7, 8, 8, 9, np.nan, 10, 11], index=idx_w)
    print(s.resample("MS").mean().round(2).tolist())   # [6.0, 8.33, 10.5]
    

    np.convolve flips the weights, which does not matter here because they are symmetric. The library version is statsmodels.tsa.seasonal.seasonal_decompose (classical) and statsmodels.tsa.seasonal.STL; the notebook compares them with your own code.

    6. Where it shows up #

    • Seasonal adjustment. Unemployment, retail sales and GDP are published seasonally adjusted, so that a change from one month to the next is not just the season.
    • Forecasting by components. Prophet fits trend + seasonality + holidays. "STL + ETS" (FPP3 §5.7) forecasts the seasonally adjusted series and adds the seasonal part back. MSTL extends STL to several seasonal periods, such as daily and weekly patterns in hourly electricity load.
    • Deep learning. Autoformer and FEDformer contain a moving-average "series decomposition block" inside the network. DLinear splits the input window into a moving-average trend and a remainder, and applies one linear layer to each (lesson 09).
    • Features for ML. FTF_T, FSF_S, the peak month and the remainder variance are standard features for classifying or clustering many series, and lags of seasonal indices are inputs for gradient-boosting forecasters (lesson 08).
    • ML for science. Climate scientists remove the mean annual cycle ("climatology") before they study anomalies such as El Niño. The Mauna Loa CO₂ record is the standard example of a trend with a stable yearly cycle; you decompose it in the notebook.
    • Anomaly detection. A large remainder RtR_t after STL is a simple and strong anomaly score (lesson 11).

    7. Common mistakes #

    • Shuffling time series data. A random split puts future values into training, and the test error is too optimistic. Split in time order (lesson 03).
    • Using a plain 12-MA as a centred trend. Its centre is half a month off. Use the 2×122 \times 12 MA (or any 2×m2 \times m MA for even mm).
    • Using a trailing MA as "the trend" in a plot. It is late by (k−1)/2(k-1)/2 steps, so peaks of the trend appear too late.
    • Additive decomposition of a multiplicative series. The seasonal swing then leaks into the remainder: large remainders at the end, where the level is high. Take logs first.
    • Calling every wave "seasonal". A season has a fixed calendar period. Business cycles and the sunspot cycle are cyclic and belong to the trend-cycle.
    • Treating decomposition as a forecast. The centred trend does not reach the last m/2m/2 points; you still need a forecasting model.
    • Ignoring missing time stamps. Code that uses "the previous row" as "the previous time step" is wrong when rows are missing. Use asfreq to make gaps visible.

    8. Check yourself #

    1. Daily data has a weekly pattern. Which moving average do you use for the trend, and at how many time steps at each end is it missing?
      AnswerThe centred 7-MA (7 is odd, so no 2×2\times step is needed). Its half-width is h=3h = 3, so the first 3 and the last 3 days have no trend value.
    2. Show that the 2×42 \times 4 MA has the weights 18,14,14,14,18\tfrac18, \tfrac14, \tfrac14, \tfrac14, \tfrac18.
      AnswerThe 4-MA over t−2,…,t+1t-2, \dots, t+1 and the 4-MA over t−1,…,t+2t-1, \dots, t+2 each give weight 14\tfrac14. Their average gives 12⋅14=18\tfrac12 \cdot \tfrac14 = \tfrac18 to yt−2y_{t-2} and yt+2y_{t+2} (in one window each), and 12⋅24=14\tfrac12 \cdot \tfrac24 = \tfrac14 to yt−1,yt,yt+1y_{t-1}, y_t, y_{t+1} (in both windows). The weights sum to 1.
    3. The raw quarterly averages of a detrended series are 2.0,−1.0,−3.5,2.12.0, -1.0, -3.5, 2.1. What are the seasonal indices?
      AnswerThe sum is −0.4-0.4, so the mean is −0.1-0.1. Subtract it: 2.1,−0.9,−3.4,2.22.1, -0.9, -3.4, 2.2. Check: 2.1−0.9−3.4+2.2=02.1 - 0.9 - 3.4 + 2.2 = 0.
    4. Hourly electricity load has a daily and a weekly pattern. What are the periods, and can classical decomposition handle both?
      AnswerThe periods are m=24m = 24 (daily) and m=168m = 168 (weekly). Classical decomposition handles one period only. Use a method for multiple seasonality, such as MSTL, which applies STL once for each period.
    5. A decomposition gives Var⁡(Rt)=4\operatorname{Var}(R_t) = 4 and Var⁡(St+Rt)=4.2\operatorname{Var}(S_t + R_t) = 4.2. What is FSF_S, and what does it say?
      AnswerFS=1−4/4.2≈0.05F_S = 1 - 4/4.2 \approx 0.05. The seasonal part adds almost nothing to the variation: the series has hardly any seasonality.
    6. Why is the remainder of an additive decomposition of an exponentially growing, multiplicative series a bad anomaly score?
      AnswerThe additive model assumes a constant seasonal swing. When the true swing grows with the level, the part of the swing that the fixed indices do not explain goes into the remainder. So the remainders grow over time and flag normal, high-level seasons as anomalies. Decompose log⁡yt\log y_t instead.
    Recall cardsAnswer in your head first, then reveal. Add the cards to your review deck to see them again just before you would forget.
    1. What are the three components of a classical decomposition?

    2. When should you use a multiplicative decomposition?

    3. Why does the log transform turn a multiplicative model into an additive one?

    4. Give the weights of the 2×122 \times 12 moving average.

    5. Why do we need a 2×m2 \times m MA when mm is even?

    6. By how many steps does a trailing moving average of order kk lag a linear trend?

    7. Compute the 2×42 \times 4 MA of the values 14, 9, 6, 15, 16 (centred on 6).

    8. What is the difference between a seasonal pattern and a cycle?

    9. What does STL stand for, and name one advantage over classical decomposition.

    10. Write the strength of seasonality FSF_S.

    Open the review →

    9. Interview angle #

    • "How do you check whether a series is seasonal?" — Time plot and seasonal plot; peaks of the ACF at multiples of mm (lesson 02); the strength FSF_S from an STL decomposition.
    • "Additive or multiplicative?" — Compare the seasonal swing at low and high levels. If it grows with the level, use multiplicative, or log + additive. Box–Cox with 0<λ<10 < \lambda < 1 is in between.
    • "Why can you not use ordinary k-fold cross-validation on time series?" — Neighbouring values are dependent and the future must not be in the training data; use rolling-origin evaluation (lesson 03).
    • "What does STL do better than classical decomposition?" — Changing seasonality, robustness to outliers, estimates at the ends, any period.
    • "What is a centred moving average of even order?" — The 2×m2 \times m MA, with half weights at both ends; explain why the plain mm-MA is half a step off.

    10. Practice #

    The notebook for this lesson runs in Colab. Each exercise has a check cell and a folded solution:

    1. Arrange a monthly series into a years × months table for the seasonal plot (seasonal_matrix), and draw the time, seasonal and lag plots of the "monthly demand" series.
    2. Write centred_ma(y, k) for odd kk and ma_2xm(y, m) for even mm, and check them against a convolution with the explicit weights.
    3. Write classical_decompose(y, m) (additive) and check that the indices sum to 0 and that the three parts add up to yy.
    4. Decompose a multiplicative series through log⁡yt\log y_t and recover its seasonal factors.
    5. Resample the weekly Mauna Loa CO₂ record to monthly means, fill the gaps, decompose it with m=12m = 12, and compare with STL (optional cell).

    11. Watch and read #

    What makes time-ordered data different from cross-sectional data.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Trend, season and error, and additive versus multiplicative models.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    What can be forecast, and the vocabulary of the FPP3 book.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    How the STL windows change the trend and the seasonal pattern.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play