Note
Go to the end to download the full example code or to run this example in your browser via JupyterLite.
Yule’s autoregression#
Schuster’s periodogram treated sunspot numbers as a sum of exact cycles plus measurement error. Udny Yule (1927) proposed instead a pendulum “bombarded by peas”: a damped oscillation kept alive by random shocks,
with \(\phi_1 \approx 1.34\) and \(\phi_2 \approx -0.65\) for Wolfer’s sunspot series. Complex roots of \(1 - \phi_1 z - \phi_2 z^2\) give a pseudo-cycle whose phase and amplitude wander, which is exactly what the sunspot record looks like. The coefficients come from the Yule-Walker equations, a Toeplitz system in the autocorrelations.
import matplotlib.pyplot as plt
import numpy as np
from financialkit.time_series import ar_cycle_period, arma_acf, autocorrelation, simulate_arma, yule_walker
A disturbed pendulum#
Two hundred “years” of Yule’s AR(2) look cyclical, but no two cycles have the same length or height.
phi = [1.34, -0.65]
x = simulate_arma(ar=phi, n=200, rng=1927)
print(f"pseudo-period implied by the roots: {ar_cycle_period(phi):.1f} years")
fig, ax = plt.subplots(figsize=(8, 3))
ax.plot(np.arange(1750, 1950), x, color="C0")
ax.axhline(0, color="k", lw=0.8)
ax.set_xlabel("year")
ax.set_ylabel("deviation")
ax.set_title("Yule's AR(2): a pendulum bombarded by random shocks")
fig.tight_layout()

pseudo-period implied by the roots: 10.7 years
Estimating it by Yule-Walker#
The sample autocorrelations oscillate and decay, as the model’s do. Solving the Yule-Walker equations on a longer simulated record recovers the coefficients and the period.
long = simulate_arma(ar=phi, n=5000, rng=1)
fit = yule_walker(long, 2)
print(f"Yule-Walker estimates: phi = {fit.ar.round(3)}, sigma^2 = {fit.sigma2:.3f}")
print(f"estimated pseudo-period: {ar_cycle_period(fit.ar):.1f} years")
fig, ax = plt.subplots()
k = np.arange(31)
ax.bar(k, autocorrelation(long, 30), color="0.7", label="sample")
ax.plot(k, arma_acf(ar=phi, nlags=30), "C3o-", ms=3, label="AR(2) theory")
ax.set_xlabel("lag (years)")
ax.set_ylabel("autocorrelation")
ax.legend()
plt.show()

Yule-Walker estimates: phi = [ 1.343 -0.651], sigma^2 = 1.021
estimated pseudo-period: 10.7 years
Total running time of the script: (0 minutes 0.074 seconds)