Skip to content

Commit 0d2accf

Browse files
authored
Merge pull request #833 from bashtage/lm71-bug7-small-fixes
BUG: Reject an unknown group in PanelData.mean, count, demean and dum…
2 parents 3bfd18f + cee0b60 commit 0d2accf

22 files changed

Lines changed: 930 additions & 101 deletions

‎linearmodels/asset_pricing/covariance.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from __future__ import annotations
66

7-
from numpy import empty, ndarray, zeros_like
7+
from numpy import empty, errstate, ndarray, zeros_like
88
from numpy.linalg import inv
99

1010
from linearmodels.iv.covariance import (
@@ -36,7 +36,10 @@ def bandwidth(self) -> float:
3636
if self._bandwidth is None:
3737
assert self._moments is not None
3838
moments = self._moments
39-
m = moments / moments.std(0)[None, :]
39+
# A moment that does not vary has a standard deviation of 0, and the
40+
# series is NaN. kernel_optimal_bandwidth raises, so do not warn
41+
with errstate(divide="ignore", invalid="ignore"):
42+
m = moments / moments.std(0)[None, :]
4043
m = m.sum(1)
4144
bw = kernel_optimal_bandwidth(m, kernel=self.kernel)
4245
self._bandwidth = bw

‎linearmodels/asset_pricing/model.py‎

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,16 @@
2525
GMMFactorModelResults,
2626
LinearFactorModelResults,
2727
)
28+
from linearmodels.iv.covariance import KERNEL_LOOKUP
2829
from linearmodels.iv.data import IVData, IVDataLike
2930
from linearmodels.shared import hypotheses
3031
from linearmodels.shared.alignment import check_alignment, row_labels
31-
from linearmodels.shared.exceptions import missing_warning, unknown_option_error
32+
from linearmodels.shared.exceptions import (
33+
check_cov_config,
34+
lower_option,
35+
missing_warning,
36+
unknown_option_error,
37+
)
3238
from linearmodels.shared.hypotheses import WaldTestStatistic
3339
from linearmodels.shared.linalg import has_constant
3440
from linearmodels.shared.rsquared import r_squared
@@ -38,6 +44,7 @@
3844

3945
# The names of the covariance estimators of the models
4046
COV_TYPES = ("heteroskedastic", "kernel", "robust")
47+
_COV_KEYS = {"heteroskedastic": (), "robust": (), "kernel": ("kernel", "bandwidth")}
4148

4249

4350
def callback_factory(
@@ -422,7 +429,7 @@ def fit(
422429
Parameters
423430
----------
424431
cov_type : {"robust", "heteroskedastic", "kernel"}, optional
425-
Name of covariance estimator. The names are case sensitive. The
432+
Name of covariance estimator. The names are not case sensitive. The
426433
default is "robust". "heteroskedastic" is the same as "robust".
427434
debiased : bool, optional
428435
Flag indicating whether to debias the covariance estimator using
@@ -446,6 +453,11 @@ def fit(
446453
If ``debiased`` is True and the number of observations is not larger
447454
than the number of factors plus one, or if the bandwidth is selected
448455
from the data and there are too few observations. See Notes.
456+
ValueError
457+
If a key of ``cov_config`` is not accepted by the covariance estimator
458+
that ``cov_type`` selects. The message names the key and lists the
459+
accepted keys. It is raised before the model is estimated. Releases
460+
before 7.1 ignored the key.
449461
450462
Notes
451463
-----
@@ -492,6 +504,8 @@ def fit(
492504
alphas and the loadings, ``risk_premia_se`` and the covariance with
493505
``debiased=False`` do not change.
494506
"""
507+
cov_type = lower_option(cov_type)
508+
check_cov_config(cov_config, cov_type, _COV_KEYS, KERNEL_LOOKUP)
495509
self._check_debiased(debiased)
496510
p = self.portfolios.ndarray
497511
f = self.factors.ndarray
@@ -933,7 +947,7 @@ def fit(
933947
Parameters
934948
----------
935949
cov_type : {"robust", "heteroskedastic", "kernel"}, optional
936-
Name of covariance estimator. The names are case sensitive. The
950+
Name of covariance estimator. The names are not case sensitive. The
937951
default is "robust". "heteroskedastic" is the same as "robust".
938952
debiased : bool, optional
939953
Flag indicating whether to debias the covariance estimator using
@@ -957,6 +971,11 @@ def fit(
957971
If ``debiased`` is True and the number of observations is not larger
958972
than the number of factors plus one, or if the bandwidth is selected
959973
from the data and there are too few observations. See Notes.
974+
ValueError
975+
If a key of ``cov_config`` is not accepted by the covariance estimator
976+
that ``cov_type`` selects. The message names the key and lists the
977+
accepted keys. It is raised before the model is estimated. Releases
978+
before 7.1 ignored the key.
960979
961980
Notes
962981
-----
@@ -972,6 +991,8 @@ def fit(
972991
the kernel with fewer observations. See
973992
:func:`~linearmodels.iv.covariance.kernel_optimal_bandwidth`.
974993
"""
994+
cov_type = lower_option(cov_type)
995+
check_cov_config(cov_config, cov_type, _COV_KEYS, KERNEL_LOOKUP)
975996
nobs, nf, nport, _, s1, s2, s3 = self._boundaries()
976997
excess_returns = not self._risk_free
977998
self._check_debiased(debiased)
@@ -1372,7 +1393,7 @@ def fit(
13721393
Maximum number of iterations when minimizing objective. Must be
13731394
positive. The default is 1000.
13741395
cov_type : {"robust", "heteroskedastic", "kernel"}, optional
1375-
Name of covariance estimator. The names are case sensitive. The
1396+
Name of covariance estimator. The names are not case sensitive. The
13761397
default is "robust". "heteroskedastic" is the same as "robust".
13771398
debiased : bool, optional
13781399
Flag indicating whether to debias the covariance estimator using
@@ -1404,6 +1425,11 @@ def fit(
14041425
If ``starting`` does not have one element for each parameter, or if
14051426
the bandwidth is selected from the data and there are too few
14061427
observations. See Notes.
1428+
ValueError
1429+
If a key of ``cov_config`` is not accepted by the covariance estimator
1430+
that ``cov_type`` selects. The message names the key and lists the
1431+
accepted keys. It is raised before the model is estimated. Releases
1432+
before 7.1 ignored the key.
14071433
14081434
Notes
14091435
-----
@@ -1420,6 +1446,8 @@ def fit(
14201446
observations. See
14211447
:func:`~linearmodels.iv.covariance.kernel_optimal_bandwidth`.
14221448
"""
1449+
cov_type = lower_option(cov_type)
1450+
check_cov_config(cov_config, cov_type, _COV_KEYS, KERNEL_LOOKUP)
14231451

14241452
nobs, n = self.portfolios.shape
14251453
k = self.factors.shape[1]

‎linearmodels/iv/absorbing.py‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@
4646
from scipy.sparse.linalg import lsmr
4747

4848
from linearmodels.iv.common import f_statistic
49+
from linearmodels.iv.covariance import KERNEL_LOOKUP
4950
from linearmodels.iv.data import IVData
5051
from linearmodels.iv.model import (
52+
_LS_COV_KEYS,
5153
COVARIANCE_ESTIMATORS,
5254
ClusteredCovariance,
5355
HeteroskedasticCovariance,
@@ -69,7 +71,11 @@
6971
reference_labels,
7072
row_labels,
7173
)
72-
from linearmodels.shared.exceptions import lookup_option, missing_warning
74+
from linearmodels.shared.exceptions import (
75+
check_cov_config,
76+
lookup_option,
77+
missing_warning,
78+
)
7379
from linearmodels.shared.hypotheses import InvalidTestStatistic, WaldTestStatistic
7480
from linearmodels.shared.inputs import as_array_like
7581
from linearmodels.shared.rsquared import r_squared
@@ -1514,6 +1520,12 @@ def fit(
15141520
UnknownOptionError
15151521
If ``cov_type`` or ``kernel`` is not supported. It is both a
15161522
``KeyError`` and a ``ValueError``.
1523+
ValueError
1524+
If a key of ``cov_config`` is not accepted by the covariance estimator
1525+
that ``cov_type`` selects. The message names the key and lists the
1526+
accepted keys. It is raised before the model is estimated. Releases
1527+
before 7.1 raised the ``TypeError`` of the constructor of the covariance
1528+
estimator.
15171529
15181530
Warns
15191531
-----
@@ -1577,6 +1589,7 @@ def fit(
15771589
changing only the covariance estimator is fast. If any of them differs,
15781590
the variables are absorbed again using the new values.
15791591
"""
1592+
check_cov_config(cov_config, cov_type, _LS_COV_KEYS, KERNEL_LOOKUP)
15801593
method = _check_method(method)
15811594
if lsmr_options and absorb_options is None:
15821595
# The options are options of LSMR, so they can not be passed to HDFE

‎linearmodels/iv/covariance.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from numpy.linalg import inv, pinv
2929

3030
from linearmodels.shared.covariance import cov_cluster, cov_kernel
31-
from linearmodels.shared.exceptions import unknown_option_error
31+
from linearmodels.shared.exceptions import lower_option, unknown_option_error
3232
from linearmodels.shared.inputs import as_ndarray
3333
from linearmodels.shared.linalg import has_constant
3434
import linearmodels.typing
@@ -244,6 +244,7 @@ def _bandwidth_rule(kernel: str) -> tuple[int, float, float]:
244244
UnknownOptionError
245245
If kernel is not one of the supported names.
246246
"""
247+
kernel = lower_option(kernel)
247248
if kernel in ("bartlett", "newey-west"):
248249
return 1, 1.1447, 2 / 9
249250
if kernel in ("qs", "andrews", "quadratic-spectral"):
@@ -792,6 +793,7 @@ def __init__(
792793
):
793794
super().__init__(x, y, z, params, debiased, kappa, extra_df)
794795
self._kernels = KERNEL_LOOKUP
796+
kernel = lower_option(kernel)
795797
self._kernel = kernel
796798
self._bandwidth = bandwidth
797799
self._auto_bandwidth = False

‎linearmodels/iv/gmm.py‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import Any
99
import warnings
1010

11-
from numpy import asarray, ndarray, unique
11+
from numpy import asarray, errstate, ndarray, unique
1212
from numpy.linalg import inv
1313

1414
from linearmodels.iv.covariance import (
@@ -21,6 +21,7 @@
2121
from linearmodels.shared.exceptions import (
2222
DefaultBandwidthWarning,
2323
lookup_option,
24+
lower_option,
2425
unknown_option_error,
2526
)
2627
import linearmodels.typing.data
@@ -315,8 +316,11 @@ def __init__(
315316
super().__init__(center, debiased)
316317
self._bandwidth = bandwidth
317318
self._orig_bandwidth = bandwidth
319+
kernel = lower_option(kernel)
318320
self._kernel = kernel
319321
self._kernels = KERNEL_LOOKUP
322+
if isinstance(kernel, str) and kernel not in KERNEL_LOOKUP:
323+
raise unknown_option_error("kernel", kernel, KERNEL_LOOKUP)
320324
# Selecting the bandwidth from the data is the default since 7.1, and it
321325
# warns since the default was nobs - 2
322326
self._default_bandwidth = bandwidth is None and optimal_bw is None
@@ -344,7 +348,10 @@ def _scores(
344348

345349
def _estimate_bandwidth(self, ze: linearmodels.typing.data.Float64Array) -> int:
346350
"""The bandwidth that is estimated from the moment conditions."""
347-
g = ze / ze.std(0)[None, :]
351+
# A moment that does not vary has a standard deviation of 0, and the series is
352+
# NaN. kernel_optimal_bandwidth raises, so there is no need to warn
353+
with errstate(divide="ignore", invalid="ignore"):
354+
g = ze / ze.std(0)[None, :]
348355
g = g.sum(1)
349356
return kernel_optimal_bandwidth(g, self._kernel)
350357

@@ -631,6 +638,7 @@ def __init__(
631638
**cov_config: str | bool,
632639
) -> None:
633640
super().__init__(x, y, z, params, debiased)
641+
cov_type = lower_option(cov_type)
634642
self._cov_type = cov_type
635643
self._cov_config = cov_config
636644
self.w = w

0 commit comments

Comments
 (0)