9.3 Cycles, several series and changing volatility
A time series can be described in time or in frequency; several series can be modelled together, each predicting the others; the spread of a series can itself change over time; and its whole behaviour can shift at a point. Each has its own tools.
Code: data and style used in this chapter
import warningsimport matplotlib.pyplot as pltimport numpy as npimport pandas as pdimport statsmodels.api as smfrom matplotlib.axes import Axesfrom scipy import optimize, signal, statsfrom statsmodels.tsa.api import VARfrom statsmodels.tsa.stattools import grangercausalitytestsfrom statly import stylestyle.use()rng = np.random.default_rng(1)warnings.filterwarnings("ignore")sun = sm.datasets.sunspots.load_pandas().data # sunspots per year, 1700 to 2008co2 = ( sm.datasets.co2.load_pandas().data.co2.resample("MS").mean().interpolate()) # Mauna Loa, monthlymacro = sm.datasets.macrodata.load_pandas().data # US quarterly economy, 1959 to 2009nile = sm.datasets.nile.load_pandas().data # annual flow of the Nile at Aswan, 1871 to 1970
9.3.1 Cycles: the periodogram
Chapter 9.1 described series in time: trend, season, autocorrelation. The same series can be described in frequency: as a sum of waves of different lengths. The periodogram shows how much of the series’ variance each wave length carries. A peak at period P means the series repeats roughly every P steps, even if the cycle is irregular and hard to see by eye.
Figure 9.3.1: Top: yearly sunspot numbers, 1700 to 2008, and their periodogram. The tallest peak is at a period of about 11 years, the solar cycle, with smaller peaks at related periods. Bottom: monthly carbon dioxide at Mauna Loa after removing the trend, and its periodogram. The peak is exactly at 12 months, with a smaller one at 6 months (the seasonal curve is not a pure sine wave).
The periodogram of a single series is noisy; smoothing it (or averaging over segments, the Welch method) gives a steadier estimate of the spectrum. Frequency methods are standard for sound, vibration, brain signals and anything driven by physical cycles.
9.3.2 Several series together: vector autoregression
Economic series move together: output, unemployment and inflation each help predict the others. A vector autoregression (VAR) extends the AR model of Chapter 9.2 to several series: each series is regressed on the past values of all of them. With two series and one lag:
The coefficients are hard to read one by one. The usual summary is the impulse response: if one series is hit by a one-time surprise, how do all the series respond over the following periods?
Figure 9.3.2: A VAR for quarterly US GDP growth, the change in unemployment and inflation, 1959 to 2009, with the number of lags chosen by AIC. Shown is the response of each series over 12 quarters to a one-time surprise of one standard deviation in GDP growth (with 95% bands). Unemployment falls for about a year after a growth surprise, then returns to normal; inflation rises a little, though its band mostly includes zero.
9.3.3 Granger causality
Series x Granger-causes y if past values of x improve the prediction of y beyond what y’s own past provides. In a VAR, that is an F-test of whether x’s lag coefficients in y’s equation are all zero.
Code
names =list(series.columns)print("| past of ↓ helps predict → | "+" | ".join(names) +" |")print("|:--|"+"--:|"*len(names))for cause in names: cells = []for effect in names:if cause == effect: cells.append("·")continue result = grangercausalitytests(series[[effect, cause]], maxlag=4, verbose=False) cells.append(f"p = {result[4][0]['ssr_ftest'][1]:.3g}")print(f"| {cause} | "+" | ".join(cells) +" |")
Table 9.3.1: Granger tests between pairs of the three series (4 lags each): does the row’s past help predict the column?
past of ↓ helps predict →
GDP growth
unemployment change
inflation
GDP growth
·
p = 0.000826
p = 0.108
unemployment change
p = 0.000536
·
p = 0.0652
inflation
p = 0.0386
p = 0.00208
·
Each p-value comes from comparing two regressions for the same series: one with only its own past, one that adds the other series’ past. The F statistic measures how much the second reduces the squared prediction errors, per added coefficient, relative to what is left:
Figure 9.3.3: Does the past of GDP growth help predict the change in unemployment? Left: squared prediction errors of the change in unemployment from its own four past values (grey) and with four past values of GDP growth added (blue); adding GDP growth lowers them by about a tenth. Right: the F statistic of that improvement against the F distribution with 4 and the residual degrees of freedom, the yardstick if GDP growth’s past added nothing.
Despite its name, Granger causality is about prediction, not cause. A third series that drives both with different delays makes the earlier one “Granger-cause” the later one, and expectations make it run backwards (a forecast storm makes people buy umbrellas, and umbrella sales then “predict” rain). Chapter 5.4 is the place for causal claims.
9.3.4 Changing volatility
In many series, especially financial returns, the level is nearly unpredictable but the size of the changes is not: calm periods and turbulent periods cluster. The returns are almost uncorrelated, while their squares are strongly autocorrelated. The GARCH(1,1) model lets today’s variance depend on yesterday’s surprise and yesterday’s variance:
with standard normal noise. α measures how strongly a shock raises tomorrow’s variance, and α + β how long the effect lasts.
Figure 9.3.4: Two thousand simulated daily returns from a GARCH(1,1) model (α = 0.1, β = 0.85). Top: the returns, with calm and turbulent stretches. Middle: autocorrelation of the returns (blue, near zero) and of the squared returns (red, clearly positive for many lags). Bottom: the true volatility σ_t (grey) and the volatility estimated by fitting the GARCH model to the returns by maximum likelihood (red), which tracks it closely.
9.3.5 Change points
Sometimes a series does not drift but jumps: a new dam, a new law, a broken sensor. A change point analysis looks for the time at which the level (or trend, or spread) changes. The CUSUM plot, the cumulative sum of deviations from the overall mean, makes a level shift visible: it rises while values are above the mean and falls while they are below, so a shift in level appears as a peak or trough at the change. Binary segmentation finds the single split that best divides the series into two parts with different means, then repeats within each part while the improvement is significant.
Code
flow = nile.volume.to_numpy()nile_years = nile.year.to_numpy().astype(int)def best_split(x: np.ndarray, margin: int=5) ->tuple[int, float]:"""Split position that minimises the total squared deviation from the two part means.""" costs = [ ((x[:k] - x[:k].mean()) **2).sum() + ((x[k:] - x[k:].mean()) **2).sum()for k inrange(margin, len(x) - margin) ] k =int(np.argmin(costs)) + marginreturn k, costs[k - margin]split, _ = best_split(flow)cusum = np.cumsum(flow - flow.mean())fig, axes = style.subplots(2, height=3.2, width=10.5)axes[0].plot(nile_years, flow, color=style.GREY, lw=1.2)axes[0].plot(nile_years[:split], np.full(split, flow[:split].mean()), color=style.CORAL, lw=2.5)axes[0].plot( nile_years[split:], np.full(len(flow) - split, flow[split:].mean()), color=style.CORAL, lw=2.5,)axes[0].axvline(nile_years[split], color=style.INK, ls="--", lw=1)style.note( axes[0],f"change at {nile_years[split]}: {flow[:split].mean():.0f} → {flow[split:].mean():.0f}", x=0.4, y=0.95, color=style.CORAL,)axes[0].set(xlabel="year", ylabel="flow (10⁸ m³)")axes[1].plot(nile_years, cusum, color=style.BLUE, lw=2)axes[1].axvline(nile_years[split], color=style.INK, ls="--", lw=1)axes[1].set(xlabel="year", ylabel="cumulative deviation")fig.tight_layout()plt.show()
Figure 9.3.5: Annual flow of the Nile at Aswan, 1871 to 1970. Left: the flow with the means before and after the best single split (red): the lower level starts in 1899, just after the first Aswan dam was begun in 1898. Right: the CUSUM of deviations from the overall mean; it peaks at the change, then falls steadily as the lower flows accumulate.
Testing for a change point at a time chosen by looking at the data is another hidden multiple test (Chapter 3.10): the best split of pure noise always looks like something. Proper tests account for having searched all possible split times, for instance by comparing with the best split found in shuffled or simulated series.
9.3.6 In one picture
The periodogram shows which cycle lengths carry the variance; peaks reveal periodic behaviour.
A VAR regresses each series on the past of all of them; read it through impulse responses.
Granger causality is improvement in prediction, not cause.
GARCH models clustering volatility: ; check the autocorrelation of squared values.
Change points: CUSUM plots and best splits; account for the search when testing.
Next: time until an event, when some events have not happened yet.