diff --git a/doc/whats_new/v0.20.rst b/doc/whats_new/v0.20.rst index b374e66e7b25e..fb1a06aa3f04a 100644 --- a/doc/whats_new/v0.20.rst +++ b/doc/whats_new/v0.20.rst @@ -172,6 +172,11 @@ Model evaluation and meta-estimators group-based CV strategies. :issue:`9085` by :user:`Laurent Direr ` and `Andreas Müller`_. +- The ``predict`` method of :class:`pipeline.Pipeline` now passes keyword + arguments on to the pipeline's last estimator, enabling the use of parameters + such as ``return_std`` in a pipeline with caution. + :issue:`9304` by :user:`Breno Freitas `. + - Add `return_estimator` parameter in :func:`model_selection.cross_validate` to return estimators fitted on each split. :issue:`9686` by :user:`Aurélien Bellet `. diff --git a/sklearn/pipeline.py b/sklearn/pipeline.py index 796de4d5543ba..67649d76d428a 100644 --- a/sklearn/pipeline.py +++ b/sklearn/pipeline.py @@ -287,7 +287,7 @@ def fit_transform(self, X, y=None, **fit_params): return last_step.fit(Xt, y, **fit_params).transform(Xt) @if_delegate_has_method(delegate='_final_estimator') - def predict(self, X): + def predict(self, X, **predict_params): """Apply transforms to the data, and predict with the final estimator Parameters @@ -296,6 +296,14 @@ def predict(self, X): Data to predict on. Must fulfill input requirements of first step of the pipeline. + **predict_params : dict of string -> object + Parameters to the ``predict`` called at the end of all + transformations in the pipeline. Note that while this may be + used to return uncertainties from some models with return_std + or return_cov, uncertainties that are generated by the + transformations in the pipeline are not propagated to the + final estimator. + Returns ------- y_pred : array-like @@ -304,7 +312,7 @@ def predict(self, X): for name, transform in self.steps[:-1]: if transform is not None: Xt = transform.transform(Xt) - return self.steps[-1][-1].predict(Xt) + return self.steps[-1][-1].predict(Xt, **predict_params) @if_delegate_has_method(delegate='_final_estimator') def fit_predict(self, X, y=None, **fit_params): diff --git a/sklearn/tests/test_pipeline.py b/sklearn/tests/test_pipeline.py index ab2108ed690f2..95172b103be7a 100644 --- a/sklearn/tests/test_pipeline.py +++ b/sklearn/tests/test_pipeline.py @@ -144,6 +144,17 @@ def fit(self, X, y): return self +class DummyEstimatorParams(BaseEstimator): + """Mock classifier that takes params on predict""" + + def fit(self, X, y): + return self + + def predict(self, X, got_attribute=False): + self.got_attribute = got_attribute + return self + + def test_pipeline_init(): # Test the various init parameters of the pipeline. assert_raises(TypeError, Pipeline) @@ -398,6 +409,16 @@ def test_fit_predict_with_intermediate_fit_params(): assert_false('should_succeed' in pipe.named_steps['transf'].fit_params) +def test_predict_with_predict_params(): + # tests that Pipeline passes predict_params to the final estimator + # when predict is invoked + pipe = Pipeline([('transf', Transf()), ('clf', DummyEstimatorParams())]) + pipe.fit(None, None) + pipe.predict(X=None, got_attribute=True) + + assert_true(pipe.named_steps['clf'].got_attribute) + + def test_feature_union(): # basic sanity check for feature union iris = load_iris()