1
- from typing import Dict , NamedTuple , Optional , Tuple
1
+ from typing import Dict , NamedTuple , Optional , Tuple , Type
2
2
3
3
import numpy as np
4
- from numpy import ones , zeros
4
+ from numpy import zeros
5
5
from numpy .linalg import lstsq
6
6
import pandas as pd
7
7
from statsmodels .tools import add_constant
@@ -25,7 +25,7 @@ def _normalize_name(name: str) -> str:
25
25
return name
26
26
27
27
28
- KERNELS = {}
28
+ KERNELS : Dict [ str , Type [ CovarianceEstimator ]] = {}
29
29
for name in kernel .__all__ :
30
30
estimator = getattr (kernel , name )
31
31
if issubclass (estimator , kernel .CovarianceEstimator ):
@@ -77,7 +77,7 @@ def __init__(
77
77
super ().__init__ (
78
78
x , bandwidth = bandwidth , df_adjust = df_adjust , center = center , weights = weights
79
79
)
80
- self ._kernel = kernel
80
+ self ._kernel_name = kernel
81
81
self ._lags = 0
82
82
self ._diagonal_lags = (0 ,) * self ._x .shape [0 ]
83
83
self ._method = method
@@ -100,6 +100,7 @@ def __init__(
100
100
f"are:\n \n { available_val } "
101
101
)
102
102
self ._kernel = KERNELS [kernel ]
103
+ self ._kernel_instance : Optional [CovarianceEstimator ] = None
103
104
104
105
# Attach for testing only
105
106
self ._ics : Dict [Tuple [int , int ], float ] = {}
@@ -162,8 +163,13 @@ def _ic_from_vars(
162
163
c = int (self ._center )
163
164
nobs , nvar = lhs .shape
164
165
_rhs = rhs [:, : (c + full_order * nvar )]
165
- params = lstsq (_rhs , lhs , rcond = None )[0 ]
166
- resids0 = lhs - _rhs @ params
166
+ if _rhs .shape [1 ] > 0 and lhs .shape [1 ] > 0 :
167
+ params = lstsq (_rhs , lhs , rcond = None )[0 ]
168
+ resids0 = lhs - _rhs @ params
169
+ else :
170
+ # Branch is a workaround of NumPy 1.15
171
+ # TODO: Remove after NumPy 1.15 dropped
172
+ resids0 = lhs
167
173
sigma = resids0 .T @ resids0 / nobs
168
174
nparam = (c + full_order * nvar ) * nvar
169
175
ics : Dict [Tuple [int , int ], float ] = {
@@ -175,8 +181,14 @@ def _ic_from_vars(
175
181
purged_indiv_lags = np .empty ((nvar , nobs , max_lag - full_order ))
176
182
for i in range (nvar ):
177
183
single = indiv_lags [i , :, full_order :]
178
- params = lstsq (_rhs , single , rcond = None )[0 ]
179
- purged_indiv_lags [i ] = single - _rhs @ params
184
+ if single .shape [1 ] > 0 and _rhs .shape [1 ] > 0 :
185
+ params = lstsq (_rhs , single , rcond = None )[0 ]
186
+ purged_indiv_lags [i ] = single - _rhs @ params
187
+ else :
188
+ # Branch is a workaround of NumPy 1.15
189
+ # TODO: Remove after NumPy 1.15 dropped
190
+ purged_indiv_lags [i ] = single
191
+
180
192
for diag_lag in range (1 , max_lag - full_order + 1 ):
181
193
resids = self ._fit_diagonal (resids0 .copy (), diag_lag , purged_indiv_lags )
182
194
sigma = resids .T @ resids / nobs
@@ -227,11 +239,17 @@ def _estimate_var(self, full_order: int, diag_order: int) -> VARModel:
227
239
ncommon = rhs .shape [1 ]
228
240
for i in range (nvar ):
229
241
full_rhs = np .hstack ([rhs , extra_lags [i ]])
230
- single_params = lstsq (full_rhs , lhs [:, i ], rcond = None )[0 ]
231
- params [i , :ncommon ] = single_params [:ncommon ]
232
- locs = ncommon + i + nvar * np .arange (extra_lags [i ].shape [1 ])
233
- params [i , locs ] = single_params [ncommon :]
234
- resids [:, i ] = lhs [:, i ] - full_rhs @ single_params
242
+ if full_rhs .shape [1 ] > 0 :
243
+ single_params = lstsq (full_rhs , lhs [:, i ], rcond = None )[0 ]
244
+ params [i , :ncommon ] = single_params [:ncommon ]
245
+ locs = ncommon + i + nvar * np .arange (extra_lags [i ].shape [1 ])
246
+ params [i , locs ] = single_params [ncommon :]
247
+ resids [:, i ] = lhs [:, i ] - full_rhs @ single_params
248
+ else :
249
+ # Branch is a workaround of NumPy 1.15
250
+ # TODO: Remove after NumPy 1.15 dropped
251
+ resids [:, i ] = lhs [:, i ]
252
+
235
253
return VARModel (resids , params , max_lag , self ._center )
236
254
237
255
def _estimate_sample_cov (self , nvar : int , nlag : int ) -> NDArray :
@@ -290,17 +308,23 @@ def _companion_form(
290
308
291
309
@property
292
310
def cov (self ) -> CovarianceEstimate :
293
- x = self ._x
294
311
common , individual = self ._select_lags ()
295
312
self ._order = (common , individual )
296
313
var_mod = self ._estimate_var (common , individual )
297
314
resids = var_mod .resids
298
315
nobs , nvar = resids .shape
299
- short_run = resids .T @ resids / nobs
316
+ self ._kernel_instance = self ._kernel (
317
+ resids , self ._bandwidth , 0 , False , self ._x_weights , self ._force_int
318
+ )
319
+ kern_cov = self ._kernel_instance .cov
320
+ short_run = kern_cov .short_run
321
+ x_orig = self ._x_orig
322
+ columns = x_orig .columns if isinstance (x_orig , pd .DataFrame ) else None
300
323
if var_mod .var_order == 0 :
301
324
# Special case VAR(0)
302
325
# TODO: Docs should reflect different DoF adjustment
303
- return CovarianceEstimate (short_run , np .zeros ((nvar , nvar )))
326
+ oss = kern_cov .one_sided_strict
327
+ return CovarianceEstimate (short_run , oss , columns )
304
328
comp_coefs , comp_var_cov = self ._companion_form (var_mod , short_run )
305
329
max_eig = np .abs (np .linalg .eigvals (comp_coefs )).max ()
306
330
if max_eig >= 1 :
@@ -328,7 +352,6 @@ def cov(self) -> CovarianceEstimate:
328
352
329
353
one_sided = one_sided [:nvar , :nvar ]
330
354
one_sided_strict = one_sided_strict [:nvar , :nvar ]
331
- columns = x .columns if isinstance (x , pd .DataFrame ) else None
332
355
333
356
return CovarianceEstimate (
334
357
short_run ,
@@ -338,14 +361,29 @@ def cov(self) -> CovarianceEstimate:
338
361
one_sided = one_sided ,
339
362
)
340
363
364
+ def _ensure_kernel_instantized (self ) -> None :
365
+ if self ._kernel_instance is None :
366
+ self .cov
367
+
368
+ @property
341
369
def bandwidth_scale (self ) -> float :
342
- return 1.0
370
+ self ._ensure_kernel_instantized ()
371
+ assert self ._kernel_instance is not None
372
+ return self ._kernel_instance .bandwidth_scale
343
373
374
+ @property
344
375
def kernel_const (self ) -> float :
345
- return 1.0
376
+ self ._ensure_kernel_instantized ()
377
+ assert self ._kernel_instance is not None
378
+ return self ._kernel_instance .kernel_const
346
379
347
380
def _weights (self ) -> NDArray :
348
- return ones (0 )
381
+ self ._ensure_kernel_instantized ()
382
+ assert self ._kernel_instance is not None
383
+ return self ._kernel_instance ._weights ()
349
384
385
+ @property
350
386
def rate (self ) -> float :
351
- return 2 / 9
387
+ self ._ensure_kernel_instantized ()
388
+ assert self ._kernel_instance is not None
389
+ return self ._kernel_instance .rate
0 commit comments