Source: Unemployment/unemployment.Rmd
The original fits a first-order autoregression and checks whether simulated unemployment series reproduce the observed sign-change behavior.
Code
from pathlib import Path
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import statsmodels.formula.api as smf
root = Path("../../ROS-Examples" )
unemp = pd.read_table(root / "Unemployment/data/unemp.txt" , sep= r" \s + " )
unemp.head()
0
1947
3.9
1
1948
3.8
2
1949
5.9
3
1950
5.3
4
1951
3.3
Plot the series
Code
fig, ax = plt.subplots()
ax.plot(unemp.year, unemp.y, color= "black" )
ax.set_xlabel("Year" )
ax.set_ylabel("Unemployment rate" )
Text(0, 0.5, 'Unemployment rate')
AR(1) fit
Code
unemp["y_lag" ] = unemp.y.shift(1 )
fit_lag = smf.ols("y ~ y_lag" , data= unemp.dropna()).fit()
fit_lag.summary()
OLS Regression Results
Dep. Variable:
y
R-squared:
0.601
Model:
OLS
Adj. R-squared:
0.595
Method:
Least Squares
F-statistic:
100.7
Date:
Sun, 31 May 2026
Prob (F-statistic):
5.54e-15
Time:
23:05:54
Log-Likelihood:
-98.513
No. Observations:
69
AIC:
201.0
Df Residuals:
67
BIC:
205.5
Df Model:
1
Covariance Type:
nonrobust
coef
std err
t
P>|t|
[0.025
0.975]
Intercept
1.3536
0.461
2.939
0.005
0.434
2.273
y_lag
0.7688
0.077
10.036
0.000
0.616
0.922
Omnibus:
20.288
Durbin-Watson:
1.582
Prob(Omnibus):
0.000
Jarque-Bera (JB):
26.717
Skew:
1.254
Prob(JB):
1.58e-06
Kurtosis:
4.733
Cond. No.
23.0
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
Simulate replicated series
Code
rng = np.random.default_rng(123 )
n = len (unemp)
S = 200
sigma = np.sqrt(fit_lag.mse_resid)
a, b = fit_lag.params["Intercept" ], fit_lag.params["y_lag" ]
y_rep = np.empty((S, n))
y_rep[:, 0 ] = unemp.y.iloc[0 ]
for s in range (S):
for t in range (1 , n):
y_rep[s, t] = a + b * y_rep[s, t- 1 ] + rng.normal(0 , sigma)
Code
fig, axs = plt.subplots(3 , 5 , figsize= (11 , 6 ), sharex= True , sharey= True )
for ax, s in zip (axs.ravel(), rng.choice(S, 15 , replace= False )):
ax.plot(unemp.year, y_rep[s], color= "black" , linewidth= 0.8 )
ax.set_title(f"sim { s} " , fontsize= 8 )
Test statistic: changes in direction
Code
def direction_changes(y):
y = np.asarray(y)
return np.sum (np.sign(y[2 :] - y[1 :- 1 ]) != np.sign(y[1 :- 1 ] - y[:- 2 ]))
test_y = direction_changes(unemp.y)
test_rep = np.apply_along_axis(direction_changes, 1 , y_rep)
print (test_y, np.mean(test_rep > test_y), np.quantile(test_rep, [0.1 , 0.5 , 0.9 ]))
Code
fig, ax = plt.subplots()
ax.hist(test_rep, bins= np.arange(test_rep.min (), test_rep.max ()+ 2 )- 0.5 , color= "lightgray" , edgecolor= "black" )
ax.axvline(test_y, color= "black" , linewidth= 2 )
ax.set_xlabel("direction changes" )
Text(0.5, 0, 'direction changes')