Skip to content

Commit 32298ee

Browse files
authored
Merge pull request #183 from DoubleML/s-extend-default-tests
Extend Default Setting Unit Tests and check propensities
2 parents 0dcd89c + 3107735 commit 32298ee

File tree

8 files changed

+55
-4
lines changed

8 files changed

+55
-4
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ share/python-wheels/
2727
*.egg
2828
MANIFEST
2929
*.idea
30+
*.vscode

doubleml/_utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import numpy as np
2+
import warnings
23

34
from sklearn.model_selection import cross_val_predict
45
from sklearn.base import clone
@@ -214,3 +215,11 @@ def _check_finite_predictions(preds, learner, learner_name, smpls):
214215
if not np.all(np.isfinite(preds[test_indices])):
215216
raise ValueError(f'Predictions from learner {str(learner)} for {learner_name} are not finite.')
216217
return
218+
219+
220+
def _check_is_propensity(preds, learner, learner_name, smpls, eps=1e-12):
221+
test_indices = np.concatenate([test_index for _, test_index in smpls])
222+
if any((preds[test_indices] < eps) | (preds[test_indices] > 1 - eps)):
223+
warnings.warn(f'Propensity predictions from learner {str(learner)} for'
224+
f' {learner_name} are close to zero or one (eps={eps}).')
225+
return

doubleml/double_ml.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ def __init__(self,
114114

115115
# also initialize bootstrap arrays with the default number of bootstrap replications
116116
self._n_rep_boot, self._boot_coef, self._boot_t_stat = self._initialize_boot_arrays(n_rep_boot=500)
117+
self._boot_method = None
117118

118119
# initialize instance attributes which are later used for iterating
119120
self._i_rep = None
@@ -181,6 +182,13 @@ def n_rep_boot(self):
181182
"""
182183
return self._n_rep_boot
183184

185+
@property
186+
def boot_method(self):
187+
"""
188+
The method to construct the bootstrap replications.
189+
"""
190+
return self._boot_method
191+
184192
@property
185193
def score(self):
186194
"""
@@ -567,6 +575,7 @@ def bootstrap(self, method='normal', n_rep_boot=500):
567575
self._boot_coef[self._i_treat, i_start:i_end], self._boot_t_stat[self._i_treat, i_start:i_end] =\
568576
self._compute_bootstrap(weights)
569577

578+
self._boot_method = method
570579
return self
571580

572581
def confint(self, joint=False, level=0.95):

doubleml/double_ml_iivm.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from .double_ml import DoubleML
66
from .double_ml_data import DoubleMLData
77
from .double_ml_score_mixins import LinearScoreMixin
8-
from ._utils import _dml_cv_predict, _get_cond_smpls, _dml_tune, _check_finite_predictions
8+
from ._utils import _dml_cv_predict, _get_cond_smpls, _dml_tune, _check_finite_predictions, _check_is_propensity
99

1010

1111
class DoubleMLIIVM(LinearScoreMixin, DoubleML):
@@ -251,6 +251,8 @@ def _nuisance_est(self, smpls, n_jobs_cv, return_models=False):
251251
'observed to be binary with values 0 and 1. Make sure that for classifiers '
252252
'probabilities and not labels are predicted.')
253253

254+
_check_is_propensity(g_hat0['preds'], self._learner['ml_g'], 'ml_g', smpls, eps=1e-12)
255+
254256
g_hat1 = _dml_cv_predict(self._learner['ml_g'], x, y, smpls=smpls_z1, n_jobs=n_jobs_cv,
255257
est_params=self._get_params('ml_g1'), method=self._predict_method['ml_g'],
256258
return_models=return_models)
@@ -265,11 +267,14 @@ def _nuisance_est(self, smpls, n_jobs_cv, return_models=False):
265267
'observed to be binary with values 0 and 1. Make sure that for classifiers '
266268
'probabilities and not labels are predicted.')
267269

270+
_check_is_propensity(g_hat1['preds'], self._learner['ml_g'], 'ml_g', smpls, eps=1e-12)
271+
268272
# nuisance m
269273
m_hat = _dml_cv_predict(self._learner['ml_m'], x, z, smpls=smpls, n_jobs=n_jobs_cv,
270274
est_params=self._get_params('ml_m'), method=self._predict_method['ml_m'],
271275
return_models=return_models)
272276
_check_finite_predictions(m_hat['preds'], self._learner['ml_m'], 'ml_m', smpls)
277+
_check_is_propensity(m_hat['preds'], self._learner['ml_m'], 'ml_m', smpls, eps=1e-12)
273278

274279
# nuisance r
275280
if self.subgroups['always_takers']:

doubleml/double_ml_irm.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from .double_ml_data import DoubleMLData
1111
from .double_ml_score_mixins import LinearScoreMixin
1212

13-
from ._utils import _dml_cv_predict, _get_cond_smpls, _dml_tune, _check_finite_predictions
13+
from ._utils import _dml_cv_predict, _get_cond_smpls, _dml_tune, _check_finite_predictions, _check_is_propensity
1414

1515

1616
class DoubleMLIRM(LinearScoreMixin, DoubleML):
@@ -227,6 +227,7 @@ def _nuisance_est(self, smpls, n_jobs_cv, return_models=False):
227227
est_params=self._get_params('ml_m'), method=self._predict_method['ml_m'],
228228
return_models=return_models)
229229
_check_finite_predictions(m_hat['preds'], self._learner['ml_m'], 'ml_m', smpls)
230+
_check_is_propensity(m_hat['preds'], self._learner['ml_m'], 'ml_m', smpls, eps=1e-12)
230231

231232
psi_a, psi_b = self._score_elements(y, d,
232233
g_hat0['preds'], g_hat1['preds'], m_hat['preds'],

doubleml/double_ml_plr.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from .double_ml import DoubleML
1010
from .double_ml_data import DoubleMLData
1111
from .double_ml_score_mixins import LinearScoreMixin
12-
from ._utils import _dml_cv_predict, _dml_tune, _check_finite_predictions
12+
from ._utils import _dml_cv_predict, _dml_tune, _check_finite_predictions, _check_is_propensity
1313

1414

1515
# To be removed in version 0.6.0
@@ -219,6 +219,8 @@ def _nuisance_est(self, smpls, n_jobs_cv, return_models=False):
219219
est_params=self._get_params('ml_m'), method=self._predict_method['ml_m'],
220220
return_models=return_models)
221221
_check_finite_predictions(m_hat['preds'], self._learner['ml_m'], 'ml_m', smpls)
222+
if self._check_learner(self._learner['ml_m'], 'ml_m', regressor=True, classifier=True):
223+
_check_is_propensity(m_hat['preds'], self._learner['ml_m'], 'ml_m', smpls, eps=1e-12)
222224

223225
if self._dml_data.binary_treats[self._dml_data.d_cols[self._i_treat]]:
224226
binary_preds = (type_of_target(m_hat['preds']) == 'binary')

doubleml/tests/test_blp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,4 @@ def test_dml_blp_ci_2(dml_blp_fixture):
8989

9090
def test_dml_blp_return_types(dml_blp_fixture):
9191
assert isinstance(dml_blp_fixture['blp_model'].__str__(), str)
92-
assert isinstance(dml_blp_fixture['blp_model'].summary, pd.DataFrame)
92+
assert isinstance(dml_blp_fixture['blp_model'].summary, pd.DataFrame)

doubleml/tests/test_doubleml_model_defaults.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,37 @@
1717
dml_irm = DoubleMLIRM(dml_data_irm, Lasso(), LogisticRegression())
1818
dml_iivm = DoubleMLIIVM(dml_data_iivm, Lasso(), LogisticRegression(), LogisticRegression())
1919

20+
dml_plr.fit()
21+
dml_pliv.fit()
22+
dml_irm.fit()
23+
dml_iivm.fit()
24+
25+
dml_plr.bootstrap()
26+
dml_pliv.bootstrap()
27+
dml_irm.bootstrap()
28+
dml_iivm.bootstrap()
29+
2030

2131
def _assert_resampling_default_settings(dml_obj):
2232
assert dml_obj.n_folds == 5
2333
assert dml_obj.n_rep == 1
2434
assert dml_obj.draw_sample_splitting
2535
assert dml_obj.apply_cross_fitting
2636

37+
# fit method
38+
assert dml_obj.predictions is None
39+
assert dml_obj.models is None
40+
41+
# bootstrap method
42+
assert dml_obj.boot_method == 'normal'
43+
assert dml_obj.n_rep_boot == 500
44+
45+
# confint method
46+
assert dml_obj.confint().equals(dml_obj.confint(joint=False, level=0.95))
47+
48+
# p_adjust method
49+
assert dml_obj.p_adjust().equals(dml_obj.p_adjust(method='romano-wolf'))
50+
2751

2852
@pytest.mark.ci
2953
def test_plr_defaults():

0 commit comments

Comments
 (0)