|
3 | 3 | import warnings
|
4 | 4 |
|
5 | 5 | from sklearn.base import is_regressor, is_classifier
|
| 6 | +from sklearn.metrics import mean_squared_error |
6 | 7 |
|
7 | 8 | from scipy.stats import norm
|
8 | 9 |
|
@@ -1038,6 +1039,71 @@ def _store_models(self, models):
|
1038 | 1039 | for learner in self.params_names:
|
1039 | 1040 | self._models[learner][self._dml_data.d_cols[self._i_treat]][self._i_rep] = models[learner]
|
1040 | 1041 |
|
| 1042 | + def evaluate_learners(self, learners=None, metric=mean_squared_error): |
| 1043 | + """ |
| 1044 | + Evaluate fitted learners for DoubleML models on crossvalidated predicitons. |
| 1045 | +
|
| 1046 | + Parameters |
| 1047 | + ---------- |
| 1048 | + learners : list |
| 1049 | + A list of strings which correspond to the nuisance functions of the model. |
| 1050 | +
|
| 1051 | + metric : callable |
| 1052 | + A callable function with inputs ``y_pred`` and ``y_true``. |
| 1053 | + Default is the euclidean distance. |
| 1054 | +
|
| 1055 | + Returns |
| 1056 | + ------- |
| 1057 | + dist : dict |
| 1058 | + A dictionary containing the evaluated metric for each learner. |
| 1059 | +
|
| 1060 | + Examples |
| 1061 | + -------- |
| 1062 | + >>> import numpy as np |
| 1063 | + >>> import doubleml as dml |
| 1064 | + >>> from sklearn.metrics import mean_absolute_error |
| 1065 | + >>> from doubleml.datasets import make_irm_data |
| 1066 | + >>> from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier |
| 1067 | + >>> np.random.seed(3141) |
| 1068 | + >>> ml_g = RandomForestRegressor(n_estimators=100, max_features=20, max_depth=5, min_samples_leaf=2) |
| 1069 | + >>> ml_m = RandomForestClassifier(n_estimators=100, max_features=20, max_depth=5, min_samples_leaf=2) |
| 1070 | + >>> data = make_irm_data(theta=0.5, n_obs=500, dim_x=20, return_type='DataFrame') |
| 1071 | + >>> obj_dml_data = dml.DoubleMLData(data, 'y', 'd') |
| 1072 | + >>> dml_irm_obj = dml.DoubleMLIRM(obj_dml_data, ml_g, ml_m) |
| 1073 | + >>> dml_irm_obj.fit() |
| 1074 | + >>> dml_irm_obj.evaluate_learners(metric=mean_absolute_error) |
| 1075 | + {'ml_g0': array([[1.13318973]]), |
| 1076 | + 'ml_g1': array([[0.91659939]]), |
| 1077 | + 'ml_m': array([[0.36350912]])} |
| 1078 | + """ |
| 1079 | + # if no learners are provided try to evaluate all learners |
| 1080 | + if learners is None: |
| 1081 | + learners = self.params_names |
| 1082 | + |
| 1083 | + # check metric |
| 1084 | + if not callable(metric): |
| 1085 | + raise TypeError('metric should be either a callable. ' |
| 1086 | + '%r was passed.' % metric) |
| 1087 | + |
| 1088 | + if all(learner in self.params_names for learner in learners): |
| 1089 | + if self.nuisance_targets is None: |
| 1090 | + raise ValueError('Apply fit() before evaluate_learners().') |
| 1091 | + else: |
| 1092 | + dist = {learner: np.full((self.n_rep, self._dml_data.n_coefs), np.nan) |
| 1093 | + for learner in learners} |
| 1094 | + for learner in learners: |
| 1095 | + for rep in range(self.n_rep): |
| 1096 | + for coef_idx in range(self._dml_data.n_coefs): |
| 1097 | + res = metric(y_pred=self.predictions[learner][:, rep, coef_idx].reshape(1, -1), |
| 1098 | + y_true=self.nuisance_targets[learner][:, rep, coef_idx].reshape(1, -1)) |
| 1099 | + if not np.isfinite(res): |
| 1100 | + raise ValueError(f'Evaluation from learner {str(learner)} is not finite.') |
| 1101 | + dist[learner][rep, coef_idx] = res |
| 1102 | + return dist |
| 1103 | + else: |
| 1104 | + raise ValueError(f'The learners have to be a subset of {str(self.params_names)}. ' |
| 1105 | + f'Learners {str(learners)} provided.') |
| 1106 | + |
1041 | 1107 | def draw_sample_splitting(self):
|
1042 | 1108 | """
|
1043 | 1109 | Draw sample splitting for DoubleML models.
|
|
0 commit comments