From f5fa3b59a0cde9d3ffeb2185f0bf954e13d17014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Carlos=20Alfaro=20Jim=C3=A9nez?= Date: Sun, 5 Jul 2020 12:17:47 +0200 Subject: [PATCH 1/4] FIX Fix requires_y tag for LogisticRegression(CV) --- sklearn/linear_model/_logistic.py | 3 +++ sklearn/linear_model/tests/test_logistic.py | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/sklearn/linear_model/_logistic.py b/sklearn/linear_model/_logistic.py index c9451c749aaea..e67c670130b6e 100644 --- a/sklearn/linear_model/_logistic.py +++ b/sklearn/linear_model/_logistic.py @@ -1497,6 +1497,9 @@ def predict_log_proba(self, X): """ return np.log(self.predict_proba(X)) + def _more_tags(self): + return {'requires_y': True} + class LogisticRegressionCV(LogisticRegression, BaseEstimator, LinearClassifierMixin): diff --git a/sklearn/linear_model/tests/test_logistic.py b/sklearn/linear_model/tests/test_logistic.py index cac28525da995..35b48122c9987 100644 --- a/sklearn/linear_model/tests/test_logistic.py +++ b/sklearn/linear_model/tests/test_logistic.py @@ -1827,3 +1827,11 @@ def test_scores_attribute_layout_elasticnet(): avg_score_lr = cross_val_score(lr, X, y, cv=cv).mean() assert avg_scores_lrcv[i, j] == pytest.approx(avg_score_lr) + + +@pytest.mark.parametrize("LogisticRegression", [LogisticRegression, + LogisticRegressionCV]) +def test_requires_y_tag_logistic_regression(LogisticRegression): + # Assert that the requires_y tag for + # LogisticRegression(CV) is properly set + assert LogisticRegression()._get_tags()["requires_y"] From f4d4fe51c079d866bccb442e43239d69a30a40c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Carlos=20Alfaro=20Jim=C3=A9nez?= Date: Sun, 5 Jul 2020 12:24:43 +0200 Subject: [PATCH 2/4] FIX Change MRO for LogisticRegression and LogisticRegressionCV --- sklearn/linear_model/_logistic.py | 13 ++++++------- sklearn/linear_model/tests/test_logistic.py | 8 -------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/sklearn/linear_model/_logistic.py b/sklearn/linear_model/_logistic.py index e67c670130b6e..da86a1755c2f1 100644 --- a/sklearn/linear_model/_logistic.py +++ b/sklearn/linear_model/_logistic.py @@ -1008,8 +1008,9 @@ def _log_reg_scoring_path(X, y, train, test, pos_class=None, Cs=10, return coefs, Cs, np.array(scores), n_iter -class LogisticRegression(BaseEstimator, LinearClassifierMixin, - SparseCoefMixin): +class LogisticRegression(LinearClassifierMixin, + SparseCoefMixin, + BaseEstimator): """ Logistic Regression (aka logit, MaxEnt) classifier. @@ -1497,12 +1498,10 @@ def predict_log_proba(self, X): """ return np.log(self.predict_proba(X)) - def _more_tags(self): - return {'requires_y': True} - -class LogisticRegressionCV(LogisticRegression, BaseEstimator, - LinearClassifierMixin): +class LogisticRegressionCV(LogisticRegression, + LinearClassifierMixin, + BaseEstimator): """Logistic Regression CV (aka logit, MaxEnt) classifier. See glossary entry for :term:`cross-validation estimator`. diff --git a/sklearn/linear_model/tests/test_logistic.py b/sklearn/linear_model/tests/test_logistic.py index 35b48122c9987..cac28525da995 100644 --- a/sklearn/linear_model/tests/test_logistic.py +++ b/sklearn/linear_model/tests/test_logistic.py @@ -1827,11 +1827,3 @@ def test_scores_attribute_layout_elasticnet(): avg_score_lr = cross_val_score(lr, X, y, cv=cv).mean() assert avg_scores_lrcv[i, j] == pytest.approx(avg_score_lr) - - -@pytest.mark.parametrize("LogisticRegression", [LogisticRegression, - LogisticRegressionCV]) -def test_requires_y_tag_logistic_regression(LogisticRegression): - # Assert that the requires_y tag for - # LogisticRegression(CV) is properly set - assert LogisticRegression()._get_tags()["requires_y"] From 9ee22b740f8ed12b7512e6a362569bcd5dee6659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Carlos=20Alfaro=20Jim=C3=A9nez?= Date: Sun, 5 Jul 2020 12:44:16 +0200 Subject: [PATCH 3/4] FIX More MROs changes --- sklearn/calibration.py | 5 +++-- sklearn/discriminant_analysis.py | 5 +++-- sklearn/svm/_classes.py | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/sklearn/calibration.py b/sklearn/calibration.py index f5d52f98438ac..eef8fe7f8487b 100644 --- a/sklearn/calibration.py +++ b/sklearn/calibration.py @@ -32,8 +32,9 @@ from .utils.validation import _deprecate_positional_args -class CalibratedClassifierCV(BaseEstimator, ClassifierMixin, - MetaEstimatorMixin): +class CalibratedClassifierCV(ClassifierMixin, + MetaEstimatorMixin, + BaseEstimator): """Probability calibration with isotonic regression or logistic regression. This class uses cross-validation to both estimate the parameters of a diff --git a/sklearn/discriminant_analysis.py b/sklearn/discriminant_analysis.py index 1d89fd08339b9..b4935306fa0fa 100644 --- a/sklearn/discriminant_analysis.py +++ b/sklearn/discriminant_analysis.py @@ -128,8 +128,9 @@ def _class_cov(X, y, priors, shrinkage=None): return cov -class LinearDiscriminantAnalysis(BaseEstimator, LinearClassifierMixin, - TransformerMixin): +class LinearDiscriminantAnalysis(LinearClassifierMixin, + TransformerMixin, + BaseEstimator): """Linear Discriminant Analysis A classifier with a linear decision boundary, generated by fitting class diff --git a/sklearn/svm/_classes.py b/sklearn/svm/_classes.py index a4b409a16974c..ad3dee1e44ae2 100644 --- a/sklearn/svm/_classes.py +++ b/sklearn/svm/_classes.py @@ -10,8 +10,9 @@ from ..utils.deprecation import deprecated -class LinearSVC(BaseEstimator, LinearClassifierMixin, - SparseCoefMixin): +class LinearSVC(LinearClassifierMixin, + SparseCoefMixin, + BaseEstimator): """Linear Support Vector Classification. Similar to SVC with parameter kernel='linear', but implemented in terms of From 8359666453f8f16bf0b821a952e5cbab9b86e58c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Carlos=20Alfaro=20Jim=C3=A9nez?= Date: Sun, 5 Jul 2020 12:59:52 +0200 Subject: [PATCH 4/4] FIX More MROs changes --- .../_hist_gradient_boosting/tests/test_gradient_boosting.py | 2 +- sklearn/ensemble/tests/test_stacking.py | 4 ++-- sklearn/metrics/_plot/tests/test_plot_precision_recall.py | 2 +- sklearn/model_selection/tests/test_search.py | 2 +- sklearn/multioutput.py | 3 ++- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/sklearn/ensemble/_hist_gradient_boosting/tests/test_gradient_boosting.py b/sklearn/ensemble/_hist_gradient_boosting/tests/test_gradient_boosting.py index fe3ce0c617c06..3cbcd4702fe30 100644 --- a/sklearn/ensemble/_hist_gradient_boosting/tests/test_gradient_boosting.py +++ b/sklearn/ensemble/_hist_gradient_boosting/tests/test_gradient_boosting.py @@ -392,7 +392,7 @@ def test_missing_values_minmax_imputation(): # The implementation of MIA as an imputation transformer was suggested by # "Remark 3" in https://arxiv.org/abs/1902.06931 - class MinMaxImputer(BaseEstimator, TransformerMixin): + class MinMaxImputer(TransformerMixin, BaseEstimator): def fit(self, X, y=None): mm = MinMaxScaler().fit(X) diff --git a/sklearn/ensemble/tests/test_stacking.py b/sklearn/ensemble/tests/test_stacking.py index f8a3f290e96b5..2f8e36a1b3abd 100644 --- a/sklearn/ensemble/tests/test_stacking.py +++ b/sklearn/ensemble/tests/test_stacking.py @@ -262,7 +262,7 @@ def test_stacking_classifier_drop_binary_prob(): assert X_meta.shape[1] == 2 -class NoWeightRegressor(BaseEstimator, RegressorMixin): +class NoWeightRegressor(RegressorMixin, BaseEstimator): def fit(self, X, y): self.reg = DummyRegressor() return self.reg.fit(X, y) @@ -271,7 +271,7 @@ def predict(self, X): return np.ones(X.shape[0]) -class NoWeightClassifier(BaseEstimator, ClassifierMixin): +class NoWeightClassifier(ClassifierMixin, BaseEstimator): def fit(self, X, y): self.clf = DummyClassifier(strategy='stratified') return self.clf.fit(X, y) diff --git a/sklearn/metrics/_plot/tests/test_plot_precision_recall.py b/sklearn/metrics/_plot/tests/test_plot_precision_recall.py index dc7c858723126..c9ea55601094a 100644 --- a/sklearn/metrics/_plot/tests/test_plot_precision_recall.py +++ b/sklearn/metrics/_plot/tests/test_plot_precision_recall.py @@ -63,7 +63,7 @@ def test_errors(pyplot): def test_error_bad_response(pyplot, response_method, msg): X, y = make_classification(n_classes=2, n_samples=50, random_state=0) - class MyClassifier(BaseEstimator, ClassifierMixin): + class MyClassifier(ClassifierMixin, BaseEstimator): def fit(self, X, y): self.fitted_ = True self.classes_ = [0, 1] diff --git a/sklearn/model_selection/tests/test_search.py b/sklearn/model_selection/tests/test_search.py index cd01916d28ea9..7c4f5a2ee9b1e 100644 --- a/sklearn/model_selection/tests/test_search.py +++ b/sklearn/model_selection/tests/test_search.py @@ -1821,7 +1821,7 @@ def test_scalar_fit_param(SearchCV, param_search): # unofficially sanctioned tolerance for scalar values in fit_params # non-regression test for: # https://github.com/scikit-learn/scikit-learn/issues/15805 - class TestEstimator(BaseEstimator, ClassifierMixin): + class TestEstimator(ClassifierMixin, BaseEstimator): def __init__(self, a=None): self.a = a diff --git a/sklearn/multioutput.py b/sklearn/multioutput.py index b1319e287a3c8..b020e165d35e8 100644 --- a/sklearn/multioutput.py +++ b/sklearn/multioutput.py @@ -60,7 +60,8 @@ def _partial_fit_estimator(estimator, X, y, classes=None, sample_weight=None, return estimator -class _MultiOutputEstimator(BaseEstimator, MetaEstimatorMixin, +class _MultiOutputEstimator(MetaEstimatorMixin, + BaseEstimator, metaclass=ABCMeta): @abstractmethod @_deprecate_positional_args