Skip to content

Commit 90953b3

Browse files
authored
Merge pull request #330 from bashtage/more-cov
TST: Improve edge case test coverage
2 parents fcabc82 + 60fe773 commit 90953b3

File tree

7 files changed

+44
-26
lines changed

7 files changed

+44
-26
lines changed

linearmodels/iv/results.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1493,7 +1493,7 @@ def c_stat(
14931493
elif isinstance(variables, str):
14941494
variable_lst = [variables]
14951495
else:
1496-
raise TypeError("variables must be a str of a list of str.")
1496+
raise TypeError("variables must be a str or a list of str.")
14971497
exog_e = c_[exog.ndarray, endog.pandas[variable_lst].values]
14981498
ex = [c for c in endog.pandas if c not in variable_lst]
14991499
endog_e = endog.pandas[ex].values

linearmodels/system/_utility.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,7 @@ def blocked_inner_prod(x: ArraySequence, s: NDArray) -> NDArray:
119119
s_ij = s[i, j]
120120
prod = s_ij * (xi.T @ xj)
121121
out[sel_i, sel_j] = prod
122-
if i != j:
123-
out[sel_j, sel_i] = prod.T
122+
out[sel_j, sel_i] = prod.T
124123

125124
return cast(np.ndarray, out)
126125

@@ -242,7 +241,9 @@ def __init__(
242241
if require_pandas and not isinstance(q, pd.Series):
243242
raise TypeError("q must be a Series")
244243
elif not isinstance(q, (pd.Series, np.ndarray)):
245-
raise TypeError("q must be a Series")
244+
raise TypeError("q must be a Series or an array")
245+
if r.shape[0] != q.shape[0]:
246+
raise ValueError("Constraint inputs are not shape compatible")
246247
q_pd = pd.Series(q, index=r_pd.index)
247248
else:
248249
q_pd = pd.Series(np.zeros(r_pd.shape[0]), index=r_pd.index)
@@ -261,8 +262,6 @@ def __str__(self) -> str:
261262
def _verify_constraints(self) -> None:
262263
r = self._ra
263264
q = self._qa
264-
if r.shape[0] != q.shape[0]:
265-
raise ValueError("Constraint inputs are not shape compatible")
266265
if self._num_params is not None:
267266
if r.shape[1] != self._num_params:
268267
raise ValueError(

linearmodels/system/model.py

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -595,9 +595,14 @@ def predict(
595595
assert self.formula is not None
596596
parser = SystemFormulaParser(self.formula, data=data, eval_env=eval_env)
597597
equations = parser.data
598-
params = np.asarray(params)
599-
if params.ndim == 1:
600-
params = params[:, None]
598+
params = np.atleast_2d(np.asarray(params))
599+
if params.shape[0] == 1:
600+
params = params.T
601+
nx = int(sum([_x.shape[1] for _x in self._x]))
602+
if params.shape[0] != nx:
603+
raise ValueError(
604+
f"Parameters must have {nx} elements; found {params.shape[0]}."
605+
)
601606
loc = 0
602607
out = AttrDict()
603608
for i, label in enumerate(self._eq_labels):
@@ -1437,23 +1442,6 @@ def multivariate_iv(
14371442
)
14381443
return cls(equations)
14391444

1440-
@classmethod
1441-
def multivariate_ls(
1442-
cls,
1443-
dependent: ArrayLike,
1444-
exog: OptionalArrayLike = None,
1445-
endog: OptionalArrayLike = None,
1446-
instruments: OptionalArrayLike = None,
1447-
) -> "IV3SLS":
1448-
"""
1449-
Deprecated. Use multivariate_iv.
1450-
"""
1451-
warnings.warn(
1452-
"multivariate_ls is deprecated and will be removed " "after July 24, 2020.",
1453-
FutureWarning,
1454-
)
1455-
return cls.multivariate_iv(dependent, exog, endog, instruments)
1456-
14571445
@classmethod
14581446
def from_formula(
14591447
cls,

linearmodels/tests/iv/test_formulas.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from linearmodels.formula import iv_2sls, iv_gmm, iv_gmm_cue, iv_liml
1010
from linearmodels.iv import IV2SLS, IVGMM, IVGMMCUE, IVLIML
11+
from linearmodels.iv._utility import IVFormulaParser
1112

1213

1314
@pytest.fixture(
@@ -298,3 +299,11 @@ def test_ols_formula(data):
298299
mod = IV2SLS.from_formula(fmla, data)
299300
res = mod.fit()
300301
assert "OLS Estimation Summary" in str(res)
302+
303+
304+
def test_iv_formula_parser(data, model_and_func, formula):
305+
parser = IVFormulaParser(formula, data)
306+
assert parser.eval_env == 2
307+
parser.eval_env = 3
308+
assert parser.eval_env == 3
309+
assert isinstance(parser.exog, DataFrame)

linearmodels/tests/iv/test_postestimation.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,15 @@ def test_c_stat_smoke(data):
122122
assert_allclose(c_stat.stat, c_stat2.stat)
123123

124124

125+
def test_c_stat_exception(data):
126+
res = IVGMM(data.dep, data.exog, data.endog, data.instr).fit(cov_type="robust")
127+
match = "variables must be a str or a list of str"
128+
with pytest.raises(TypeError, match=match):
129+
res.c_stat(variables=1)
130+
with pytest.raises(TypeError, match=match):
131+
res.c_stat(variables=("x1", "x2"))
132+
133+
125134
def test_linear_restriction(data):
126135
res = IV2SLS(data.dep, data.exog, data.endog, data.instr).fit(cov_type="robust")
127136
nvar = len(res.params)

linearmodels/tests/system/test_sur.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,10 @@ def test_gls_without_mv_ols_equiv(mvreg_data):
375375
def test_ols_against_gls(data):
376376
mod = SUR(data)
377377
res = mod.fit(method="gls")
378+
if isinstance(data[list(data.keys())[0]], dict):
379+
predictions = mod.predict(res.params, equations=data)
380+
predictions2 = mod.predict(np.asarray(res.params)[:, None], equations=data)
381+
assert_allclose(predictions, predictions2)
378382
sigma = res.sigma
379383
sigma_m12 = inv_matrix_sqrt(np.asarray(sigma))
380384
key = list(data.keys())[0]
@@ -688,6 +692,9 @@ def test_fitted(data):
688692
def test_predict(missing_data):
689693
mod = SUR(missing_data)
690694
res = mod.fit()
695+
pred_no_missing = res.predict(equations=missing_data, missing=False, dataframe=True)
696+
pred_missing = res.predict(equations=missing_data, missing=True, dataframe=True)
697+
assert pred_missing.shape[0] <= pred_no_missing.shape[0]
691698
pred = res.predict()
692699
for key in pred:
693700
assert_series_equal(

linearmodels/tests/system/test_utility.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ def test_linear_constraint_errors():
147147
LinearConstraint(r_df, q)
148148
with pytest.raises(TypeError):
149149
LinearConstraint(r_df, [0, 0])
150+
with pytest.raises(TypeError, match="q must be a Series or"):
151+
LinearConstraint(r=np.eye(2), q=[0, 0], require_pandas=False)
152+
with pytest.raises(ValueError, match="r is incompatible"):
153+
LinearConstraint(r=np.eye(4), q=np.zeros(4), require_pandas=False, num_params=5)
154+
with pytest.raises(ValueError, match="Constraint inputs are"):
155+
LinearConstraint(r=np.eye(4), q=np.zeros(2), require_pandas=False)
150156

151157

152158
def test_linear_constraint_repr():

0 commit comments

Comments
 (0)