From eeb5a3ba65f46c06e2db14cbcb15ba962915eb73 Mon Sep 17 00:00:00 2001 From: "Thomas J. Fan" Date: Sat, 30 Jan 2021 21:21:08 -0500 Subject: [PATCH 1/3] FIX MultiOutputRegressor correctly ducktypes fitted estimators --- sklearn/multioutput.py | 2 +- sklearn/tests/test_multioutput.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/sklearn/multioutput.py b/sklearn/multioutput.py index 9987c01b13187..4cb01c524d59d 100644 --- a/sklearn/multioutput.py +++ b/sklearn/multioutput.py @@ -198,7 +198,7 @@ def predict(self, X): Note: Separate models are generated for each predictor. """ check_is_fitted(self) - if not hasattr(self.estimator, "predict"): + if not hasattr(self.estimators_[0], "predict"): raise ValueError("The base estimator should implement" " a predict method") diff --git a/sklearn/tests/test_multioutput.py b/sklearn/tests/test_multioutput.py index edfcdef1bf89c..294cc8c7a8ee8 100644 --- a/sklearn/tests/test_multioutput.py +++ b/sklearn/tests/test_multioutput.py @@ -13,6 +13,7 @@ from sklearn import datasets from sklearn.base import clone from sklearn.datasets import make_classification +from sklearn.datasets import load_linnerud from sklearn.ensemble import GradientBoostingRegressor, RandomForestClassifier from sklearn.exceptions import NotFittedError from sklearn.linear_model import Lasso @@ -33,6 +34,7 @@ from sklearn.dummy import DummyRegressor, DummyClassifier from sklearn.pipeline import make_pipeline from sklearn.impute import SimpleImputer +from sklearn.ensemble import StackingRegressor def test_multi_target_regression(): @@ -648,3 +650,19 @@ def test_classifier_chain_tuple_invalid_order(): with pytest.raises(ValueError, match='invalid order'): chain.fit(X, y) + + +def test_multioutputregressor_ducktypes_fitted_estimator(): + """Test that MultiOutputRegressor checks the fitted estimator for + predict. Non-regression test for #16549.""" + X, y = load_linnerud(return_X_y=True) + stacker = StackingRegressor( + estimators=[("sgd", SGDRegressor(random_state=1))], + final_estimator=Ridge(), + cv=2 + ) + + reg = MultiOutputRegressor(estimator=stacker).fit(X, y) + + # Does not raise + reg.predict(X) From 201bf821a725c3fef5bf42bb43604b1bf523820a Mon Sep 17 00:00:00 2001 From: "Thomas J. Fan" Date: Sat, 30 Jan 2021 21:25:27 -0500 Subject: [PATCH 2/3] DOC Adds whats new --- doc/whats_new/v0.24.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/whats_new/v0.24.rst b/doc/whats_new/v0.24.rst index f549b31f51aa7..20ccbef056edc 100644 --- a/doc/whats_new/v0.24.rst +++ b/doc/whats_new/v0.24.rst @@ -20,6 +20,13 @@ Changelog `'use_encoded_value'` strategies. :pr:`19234` by `Guillaume Lemaitre `. +:mod:`sklearn.multioutput` +.......................... + +- |Fix| :class:`multioutput.MultiOutputRegressor` now works with estimators + that dynamically define `predict` during fitted, such as + :class:`ensemble.StackingRegressor`. :pr:`19308` by `Thomas Fan`_. + .. _changes_0_24_1: Version 0.24.1 From 4b38c7088d223425f2799ed7bedb99f445dba070 Mon Sep 17 00:00:00 2001 From: "Thomas J. Fan" Date: Wed, 7 Apr 2021 09:34:16 -0400 Subject: [PATCH 3/3] Update doc/whats_new/v0.24.rst Co-authored-by: Olivier Grisel --- doc/whats_new/v0.24.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/whats_new/v0.24.rst b/doc/whats_new/v0.24.rst index 6972cdea2e667..2cfe6970dd7b1 100644 --- a/doc/whats_new/v0.24.rst +++ b/doc/whats_new/v0.24.rst @@ -52,7 +52,7 @@ Changelog .......................... - |Fix| :class:`multioutput.MultiOutputRegressor` now works with estimators - that dynamically define `predict` during fitted, such as + that dynamically define `predict` during fitting, such as :class:`ensemble.StackingRegressor`. :pr:`19308` by `Thomas Fan`_. :mod:`sklearn.semi_supervised`