1- from typing import Dict , NamedTuple , Optional , Tuple
1+ from typing import Dict , NamedTuple , Optional , Tuple , Type
22
33import numpy as np
4- from numpy import ones , zeros
4+ from numpy import zeros
55from numpy .linalg import lstsq
66import pandas as pd
77from statsmodels .tools import add_constant
@@ -25,7 +25,7 @@ def _normalize_name(name: str) -> str:
2525 return name
2626
2727
28- KERNELS = {}
28+ KERNELS : Dict [ str , Type [ CovarianceEstimator ]] = {}
2929for name in kernel .__all__ :
3030 estimator = getattr (kernel , name )
3131 if issubclass (estimator , kernel .CovarianceEstimator ):
@@ -77,7 +77,7 @@ def __init__(
7777 super ().__init__ (
7878 x , bandwidth = bandwidth , df_adjust = df_adjust , center = center , weights = weights
7979 )
80- self ._kernel = kernel
80+ self ._kernel_name = kernel
8181 self ._lags = 0
8282 self ._diagonal_lags = (0 ,) * self ._x .shape [0 ]
8383 self ._method = method
@@ -100,6 +100,7 @@ def __init__(
100100 f"are:\n \n { available_val } "
101101 )
102102 self ._kernel = KERNELS [kernel ]
103+ self ._kernel_instance : Optional [CovarianceEstimator ] = None
103104
104105 # Attach for testing only
105106 self ._ics : Dict [Tuple [int , int ], float ] = {}
@@ -162,8 +163,13 @@ def _ic_from_vars(
162163 c = int (self ._center )
163164 nobs , nvar = lhs .shape
164165 _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
167173 sigma = resids0 .T @ resids0 / nobs
168174 nparam = (c + full_order * nvar ) * nvar
169175 ics : Dict [Tuple [int , int ], float ] = {
@@ -175,8 +181,14 @@ def _ic_from_vars(
175181 purged_indiv_lags = np .empty ((nvar , nobs , max_lag - full_order ))
176182 for i in range (nvar ):
177183 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+
180192 for diag_lag in range (1 , max_lag - full_order + 1 ):
181193 resids = self ._fit_diagonal (resids0 .copy (), diag_lag , purged_indiv_lags )
182194 sigma = resids .T @ resids / nobs
@@ -227,11 +239,17 @@ def _estimate_var(self, full_order: int, diag_order: int) -> VARModel:
227239 ncommon = rhs .shape [1 ]
228240 for i in range (nvar ):
229241 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+
235253 return VARModel (resids , params , max_lag , self ._center )
236254
237255 def _estimate_sample_cov (self , nvar : int , nlag : int ) -> NDArray :
@@ -290,17 +308,23 @@ def _companion_form(
290308
291309 @property
292310 def cov (self ) -> CovarianceEstimate :
293- x = self ._x
294311 common , individual = self ._select_lags ()
295312 self ._order = (common , individual )
296313 var_mod = self ._estimate_var (common , individual )
297314 resids = var_mod .resids
298315 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
300323 if var_mod .var_order == 0 :
301324 # Special case VAR(0)
302325 # 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 )
304328 comp_coefs , comp_var_cov = self ._companion_form (var_mod , short_run )
305329 max_eig = np .abs (np .linalg .eigvals (comp_coefs )).max ()
306330 if max_eig >= 1 :
@@ -328,7 +352,6 @@ def cov(self) -> CovarianceEstimate:
328352
329353 one_sided = one_sided [:nvar , :nvar ]
330354 one_sided_strict = one_sided_strict [:nvar , :nvar ]
331- columns = x .columns if isinstance (x , pd .DataFrame ) else None
332355
333356 return CovarianceEstimate (
334357 short_run ,
@@ -338,14 +361,29 @@ def cov(self) -> CovarianceEstimate:
338361 one_sided = one_sided ,
339362 )
340363
364+ def _ensure_kernel_instantized (self ) -> None :
365+ if self ._kernel_instance is None :
366+ self .cov
367+
368+ @property
341369 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
343373
374+ @property
344375 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
346379
347380 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 ()
349384
385+ @property
350386 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