Skip to content

Commit a3e0951

Browse files
committed
More work and tests
1 parent cc62df7 commit a3e0951

File tree

10 files changed

+1385
-1237
lines changed

10 files changed

+1385
-1237
lines changed

arch/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
pytest_plugins = [
44
"arch.tests.unitroot.cointegration_data",
5+
"arch.tests.covariance.covariance_data",
56
]
67

78

arch/covariance/kernel.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ class CovarianceEstimator(ABC):
189189
def __init__(
190190
self,
191191
x: ArrayLike,
192+
*,
192193
bandwidth: Optional[float] = None,
193194
df_adjust: int = 0,
194195
center: bool = True,

arch/covariance/var.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from typing import Dict, NamedTuple, Optional, Tuple
22

33
import numpy as np
4-
from numpy import zeros
54
from numpy.linalg import lstsq
65
import pandas as pd
76
from statsmodels.tools import add_constant
@@ -38,6 +37,7 @@ class PreWhitenRecoloredCovariance(CovarianceEstimator):
3837
df_adjust : int, default 0
3938
center : bool, default True
4039
weights : array_like, default None
40+
force_int: bool, default False
4141
4242
See Also
4343
--------
@@ -52,6 +52,7 @@ class PreWhitenRecoloredCovariance(CovarianceEstimator):
5252
def __init__(
5353
self,
5454
x: ArrayLike,
55+
*,
5556
lags: Optional[int] = None,
5657
method: str = "aic",
5758
diagonal: bool = True,
@@ -62,9 +63,15 @@ def __init__(
6263
df_adjust: int = 0,
6364
center: bool = True,
6465
weights: Optional[ArrayLike] = None,
66+
force_int: bool = False,
6567
) -> None:
6668
super().__init__(
67-
x, bandwidth=bandwidth, df_adjust=df_adjust, center=center, weights=weights
69+
x,
70+
bandwidth=bandwidth,
71+
df_adjust=df_adjust,
72+
center=center,
73+
weights=weights,
74+
force_int=force_int,
6875
)
6976
self._kernel_name = kernel
7077
self._lags = 0
@@ -155,7 +162,7 @@ def _ic_from_vars(
155162
ics: Dict[Tuple[int, int], float] = {
156163
(full_order, full_order): self._ic(sigma, nparam, nobs)
157164
}
158-
if not self._diagonal:
165+
if not self._diagonal or self._x.shape[1] == 1:
159166
return ics
160167

161168
purged_indiv_lags = np.empty((nvar, nobs, max_lag - full_order))
@@ -214,7 +221,7 @@ def _estimate_var(self, full_order: int, diag_order: int) -> VARModel:
214221
rhs = rhs[:, : c + full_order * nvar]
215222
extra_lags = extra_lags[:, :, full_order:diag_order]
216223

217-
params = zeros((nvar, nvar * max_lag + center))
224+
params = np.zeros((nvar, nvar * max_lag + center))
218225
resids = np.empty_like(lhs)
219226
ncommon = rhs.shape[1]
220227
for i in range(nvar):
@@ -246,8 +253,8 @@ def _estimate_sample_cov(self, nvar: int, nlag: int) -> NDArray:
246253
if self._center:
247254
x = x - x.mean(0)
248255
nobs = x.shape[0]
249-
var_cov = zeros((nvar * nlag, nvar * nlag))
250-
gamma = zeros((nlag, nvar, nvar))
256+
var_cov = np.zeros((nvar * nlag, nvar * nlag))
257+
gamma = np.zeros((nlag, nvar, nvar))
251258
for i in range(nlag):
252259
gamma[i] = (x[i:].T @ x[: (nobs - i)]) / nobs
253260
for r in range(nlag):
@@ -261,7 +268,7 @@ def _estimate_sample_cov(self, nvar: int, nlag: int) -> NDArray:
261268
def _estimate_model_cov(
262269
self, nvar: int, nlag: int, coeffs: NDArray, short_run: NDArray
263270
) -> NDArray:
264-
sigma = zeros((nvar * nlag, nvar * nlag))
271+
sigma = np.zeros((nvar * nlag, nvar * nlag))
265272
sigma[:nvar, :nvar] = short_run
266273
multiplier = np.linalg.inv(np.eye(coeffs.size) - np.kron(coeffs, coeffs))
267274
vec_sigma = sigma.ravel()[:, None]
@@ -274,7 +281,7 @@ def _companion_form(
274281
) -> Tuple[NDArray, NDArray]:
275282
nvar = var_model.resids.shape[1]
276283
nlag = var_model.var_order
277-
coeffs = zeros((nvar * nlag, nvar * nlag))
284+
coeffs = np.zeros((nvar * nlag, nvar * nlag))
278285
coeffs[:nvar] = var_model.params[:, var_model.intercept :]
279286
for i in range(nlag - 1):
280287
coeffs[(i + 1) * nvar : (i + 2) * nvar, i * nvar : (i + 1) * nvar] = np.eye(
@@ -294,7 +301,12 @@ def cov(self) -> CovarianceEstimate:
294301
resids = var_mod.resids
295302
nobs, nvar = resids.shape
296303
self._kernel_instance = self._kernel(
297-
resids, self._bandwidth, 0, False, self._x_weights, self._force_int
304+
resids,
305+
bandwidth=self._bandwidth,
306+
df_adjust=0,
307+
center=False,
308+
weights=self._x_weights,
309+
force_int=self._force_int,
298310
)
299311
kern_cov = self._kernel_instance.cov
300312
short_run = kern_cov.short_run
@@ -316,7 +328,7 @@ def cov(self) -> CovarianceEstimate:
316328
have diagonal coefficient matrices. The maximum eigenvalue of the companion-form \
317329
VAR(1) coefficient matrix is {max_eig}."""
318330
)
319-
coeff_sum = zeros((nvar, nvar))
331+
coeff_sum = np.zeros((nvar, nvar))
320332
params = var_mod.params[:, var_mod.intercept :]
321333
for i in range(var_mod.var_order):
322334
coeff_sum += params[:, i * nvar : (i + 1) * nvar]
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from itertools import product
2+
3+
import numpy as np
4+
import pandas as pd
5+
import pytest
6+
7+
DATA_PARAMS = list(product([1, 3], [True, False], [0, 1, 3]))
8+
DATA_IDS = [f"dim: {d}, pandas: {p}, order: {o}" for d, p, o in DATA_PARAMS]
9+
10+
11+
@pytest.fixture(scope="module", params=DATA_PARAMS, ids=DATA_IDS)
12+
def covariance_data(request):
13+
dim, pandas, order = request.param
14+
rs = np.random.RandomState([839084, 3823810, 982103, 829108])
15+
burn = 100
16+
shape = (burn + 500,)
17+
if dim > 1:
18+
shape += (3,)
19+
rvs = rs.standard_normal(shape)
20+
phi = np.zeros((order, dim, dim))
21+
if order > 0:
22+
phi[0] = np.eye(dim) * 0.4 + 0.1
23+
for i in range(1, order):
24+
phi[i] = 0.3 / (i + 1) * np.eye(dim)
25+
for i in range(order, burn + 500):
26+
for j in range(order):
27+
if dim == 1:
28+
rvs[i] += np.squeeze(phi[j] * rvs[i - j - 1])
29+
else:
30+
rvs[i] += phi[j] @ rvs[i - j - 1]
31+
if order > 1:
32+
p = np.eye(dim * order, dim * order, -dim)
33+
for j in range(order):
34+
p[:dim, j * dim : (j + 1) * dim] = phi[j]
35+
v, _ = np.linalg.eig(p)
36+
assert np.max(np.abs(v)) < 1
37+
rvs = rvs[burn:]
38+
if pandas and dim == 1:
39+
return pd.Series(rvs, name="x")
40+
elif pandas:
41+
df = pd.DataFrame(rvs, columns=[f"x{i}" for i in range(dim)])
42+
df.to_csv(f"cov-data-order-{order}.csv")
43+
return df
44+
45+
return rvs

arch/tests/covariance/test_var.py

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
from itertools import product
21
from typing import Optional, Tuple
32

43
import numpy as np
54
from numpy.testing import assert_allclose
65
import pandas as pd
76
import pytest
87

8+
from arch.covariance.kernel import CovarianceEstimate
99
from arch.covariance.var import PreWhitenRecoloredCovariance
1010
from arch.typing import NDArray
1111

12-
DATA_PARAMS = list(product([1, 3], [True, False], [0])) # , 1, 3]))
13-
DATA_IDS = [f"dim: {d}, pandas: {p}, order: {o}" for d, p, o in DATA_PARAMS]
1412
KERNELS = [
1513
"Bartlett",
1614
"Parzen",
@@ -32,40 +30,6 @@ def kernel(request):
3230
return request.param
3331

3432

35-
@pytest.fixture(scope="module", params=DATA_PARAMS, ids=DATA_IDS)
36-
def data(request):
37-
dim, pandas, order = request.param
38-
rs = np.random.RandomState([839084, 3823810, 982103, 829108])
39-
burn = 100
40-
shape = (burn + 500,)
41-
if dim > 1:
42-
shape += (3,)
43-
rvs = rs.standard_normal(shape)
44-
phi = np.zeros((order, dim, dim))
45-
if order > 0:
46-
phi[0] = np.eye(dim) * 0.4 + 0.1
47-
for i in range(1, order):
48-
phi[i] = 0.3 / (i + 1) * np.eye(dim)
49-
for i in range(order, burn + 500):
50-
for j in range(order):
51-
if dim == 1:
52-
rvs[i] += np.squeeze(phi[j] * rvs[i - j - 1])
53-
else:
54-
rvs[i] += phi[j] @ rvs[i - j - 1]
55-
if order > 1:
56-
p = np.eye(dim * order, dim * order, -dim)
57-
for j in range(order):
58-
p[:dim, j * dim : (j + 1) * dim] = phi[j]
59-
v, _ = np.linalg.eig(p)
60-
assert np.max(np.abs(v)) < 1
61-
rvs = rvs[burn:]
62-
if pandas and dim == 1:
63-
return pd.Series(rvs, name="x")
64-
elif pandas:
65-
return pd.DataFrame(rvs, columns=[f"x{i}" for i in range(dim)])
66-
return rvs
67-
68-
6933
def direct_var(
7034
x, const: bool, full_order: int, diag_order: int, max_order: Optional[int] = None
7135
) -> Tuple[NDArray, NDArray]:
@@ -140,29 +104,38 @@ def direct_ic(
140104
@pytest.mark.parametrize("diag_order", [3, 5])
141105
@pytest.mark.parametrize("max_order", [None, 10])
142106
@pytest.mark.parametrize("ic", ["aic", "bic", "hqc"])
143-
def test_direct_var(data, const, full_order, diag_order, max_order, ic):
144-
direct_ic(data, ic, const, full_order, diag_order, max_order)
107+
def test_direct_var(covariance_data, const, full_order, diag_order, max_order, ic):
108+
direct_ic(covariance_data, ic, const, full_order, diag_order, max_order)
145109

146110

147111
@pytest.mark.parametrize("center", [True, False])
148112
@pytest.mark.parametrize("diagonal", [True, False])
149113
@pytest.mark.parametrize("method", ["aic", "bic", "hqc"])
150-
def test_ic(data, center, diagonal, method):
114+
def test_ic(covariance_data, center, diagonal, method):
151115
pwrc = PreWhitenRecoloredCovariance(
152-
data, center=center, diagonal=diagonal, method=method, bandwidth=0.0,
116+
covariance_data, center=center, diagonal=diagonal, method=method, bandwidth=0.0,
153117
)
154118
cov = pwrc.cov
155-
expected_type = np.ndarray if isinstance(data, np.ndarray) else pd.DataFrame
119+
expected_type = (
120+
np.ndarray if isinstance(covariance_data, np.ndarray) else pd.DataFrame
121+
)
156122
assert isinstance(cov.short_run, expected_type)
157-
expected_max_lag = int(data.shape[0] ** (1 / 3))
123+
expected_max_lag = int(covariance_data.shape[0] ** (1 / 3))
158124
assert pwrc._max_lag == expected_max_lag
159125
expected_ics = {}
160126
for full_order in range(expected_max_lag + 1):
161127
diag_limit = expected_max_lag + 1 if diagonal else full_order + 1
128+
if covariance_data.ndim == 1 or covariance_data.shape[1] == 1:
129+
diag_limit = full_order + 1
162130
for diag_order in range(full_order, diag_limit):
163131
key = (full_order, diag_order)
164132
expected_ics[key] = direct_ic(
165-
data, method, center, full_order, diag_order, max_order=expected_max_lag
133+
covariance_data,
134+
method,
135+
center,
136+
full_order,
137+
diag_order,
138+
max_order=expected_max_lag,
166139
)
167140
assert tuple(sorted(pwrc._ics.keys())) == tuple(sorted(expected_ics.keys()))
168141
for key in expected_ics:
@@ -175,13 +148,18 @@ def test_ic(data, center, diagonal, method):
175148
@pytest.mark.parametrize("diagonal", [True, False])
176149
@pytest.mark.parametrize("method", ["aic", "bic", "hqc"])
177150
@pytest.mark.parametrize("lags", [0, 1, 3])
178-
def test_short_long_run(data, center, diagonal, method, lags):
151+
def test_short_long_run(covariance_data, center, diagonal, method, lags):
179152
pwrc = PreWhitenRecoloredCovariance(
180-
data, center=center, diagonal=diagonal, method=method, lags=lags, bandwidth=0.0,
153+
covariance_data,
154+
center=center,
155+
diagonal=diagonal,
156+
method=method,
157+
lags=lags,
158+
bandwidth=0.0,
181159
)
182160
cov = pwrc.cov
183161
full_order, diag_order = pwrc._order
184-
params, resids = direct_var(data, center, full_order, diag_order)
162+
params, resids = direct_var(covariance_data, center, full_order, diag_order)
185163
nobs, nvar = resids.shape
186164
expected_short_run = resids.T @ resids / nobs
187165
assert_allclose(cov.short_run, expected_short_run)
@@ -195,12 +173,29 @@ def test_short_long_run(data, center, diagonal, method, lags):
195173
assert_allclose(cov.long_run, expected_long_run)
196174

197175

176+
@pytest.mark.parametrize("force_int", [True, False])
177+
def test_pwrc_attributes(covariance_data, force_int):
178+
pwrc = PreWhitenRecoloredCovariance(covariance_data, force_int=force_int)
179+
assert isinstance(pwrc.bandwidth_scale, float)
180+
assert isinstance(pwrc.kernel_const, float)
181+
assert isinstance(pwrc.rate, float)
182+
assert isinstance(pwrc._weights(), np.ndarray)
183+
assert pwrc.force_int == force_int
184+
expected_type = (
185+
np.ndarray if isinstance(covariance_data, np.ndarray) else pd.DataFrame
186+
)
187+
assert isinstance(pwrc.cov.short_run, expected_type)
188+
assert isinstance(pwrc.cov.long_run, expected_type)
189+
assert isinstance(pwrc.cov.one_sided, expected_type)
190+
assert isinstance(pwrc.cov.one_sided_strict, expected_type)
191+
192+
198193
@pytest.mark.parametrize("sample_autocov", [True, False])
199-
def test_data(data, sample_autocov):
194+
def test_data(covariance_data, sample_autocov, kernel):
200195
pwrc = PreWhitenRecoloredCovariance(
201-
data, sample_autocov=sample_autocov, bandwidth=0.0
196+
covariance_data, sample_autocov=sample_autocov, kernel=kernel, bandwidth=0.0
202197
)
203-
pwrc.cov
198+
assert isinstance(pwrc.cov, CovarianceEstimate)
204199

205200

206201
def test_pwrc_errors():
@@ -216,4 +211,9 @@ def test_pwrc_errors():
216211
def test_pwrc_warnings():
217212
x = np.random.standard_normal((9, 5))
218213
with pytest.warns(RuntimeWarning, match="The maximum number of lags is 0"):
219-
PreWhitenRecoloredCovariance(x).cov
214+
assert isinstance(PreWhitenRecoloredCovariance(x).cov, CovarianceEstimate)
215+
216+
217+
def test_unknown_kernel(covariance_data):
218+
with pytest.raises(ValueError, match=""):
219+
PreWhitenRecoloredCovariance(covariance_data, kernel="unknown")

arch/tests/unitroot/test_dynamic_ols.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,17 @@ def test_smoke(data, trend, lags, leads, common, max_lag, method):
1616
y, x = data
1717
if common:
1818
leads = lags
19-
mod = DynamicOLS(y, x, trend, lags, leads, common, max_lag, max_lag, method)
19+
mod = DynamicOLS(
20+
y,
21+
x,
22+
trend,
23+
lags=lags,
24+
leads=leads,
25+
common=common,
26+
max_lag=max_lag,
27+
max_lead=max_lag,
28+
method=method,
29+
)
2030
mod.fit()
2131

2232

@@ -27,8 +37,14 @@ def test_smoke(data, trend, lags, leads, common, max_lag, method):
2737
@pytest.mark.parametrize("df_adjust", [True, False])
2838
def test_smoke_fit(data, cov_type, kernel, bandwidth, force_int, df_adjust):
2939
y, x = data
30-
mod = DynamicOLS(y, x, "ct", 3, 5, False)
31-
res = mod.fit(cov_type, kernel, bandwidth, force_int, df_adjust)
40+
mod = DynamicOLS(y, x, "ct", lags=3, leads=5, common=False)
41+
res = mod.fit(
42+
cov_type,
43+
kernel=kernel,
44+
bandwidth=bandwidth,
45+
force_int=force_int,
46+
df_adjust=df_adjust,
47+
)
3248
assert isinstance(res.leads, int)
3349
assert isinstance(res.lags, int)
3450
assert isinstance(res.bandwidth, (int, float))
@@ -44,7 +60,7 @@ def test_smoke_fit(data, cov_type, kernel, bandwidth, force_int, df_adjust):
4460
def test_mismatch_lead_lag(data):
4561
y, x = data
4662
with pytest.raises(ValueError, match="common is specified but leads"):
47-
DynamicOLS(y, x, "c", 4, 5, True)
63+
DynamicOLS(y, x, "c", lags=4, leads=5, common=True)
4864
with pytest.raises(ValueError, match="common is specified but max_lead"):
4965
DynamicOLS(y, x, max_lag=6, max_lead=7, common=True)
5066

0 commit comments

Comments
 (0)