Thanks to visit codestin.com
Credit goes to github.com

Skip to content

MNT Deprecates _estimator_type and replaces by a estimator tag #17806

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 49 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
0412969
MNT Deprecate _estimator_type and replace by estimator_tags
alfaro96 Jul 1, 2020
2aead16
MNT Replace _estimator_type attribute in glossary by estimator_type tag
alfaro96 Jul 1, 2020
a907782
MNT Replace _estimator_type attribute in develop by estimator_type tag
alfaro96 Jul 1, 2020
d501d1c
FIX Fix lint
alfaro96 Jul 1, 2020
f91c369
FIX Fix lint
alfaro96 Jul 1, 2020
17f581e
MNT Minor change
alfaro96 Jul 1, 2020
376231f
FIX Remove estimator_type from DEFAULT_TAGS
alfaro96 Jul 3, 2020
99766bb
MNT Use simpler solution
alfaro96 Jul 3, 2020
8158a5f
FIX Check if estimator has estimator_type attribute
alfaro96 Jul 3, 2020
24c6fe4
MNT Use getattr to return a dict if _get_tags is not defined
alfaro96 Jul 3, 2020
2b732e5
FIX Minor changes
alfaro96 Jul 3, 2020
9d55a16
FIX Fix changes to make the CI to pass
alfaro96 Jul 3, 2020
05fe203
DOC Add meaning of estimator_type to developing estimators
alfaro96 Jul 3, 2020
56cc0f0
Trigger CI again
alfaro96 Jul 3, 2020
7540663
CLN Apply suggested changes
alfaro96 Jul 5, 2020
c34742f
Merge remote-tracking branch 'upstream/master' into deprecate_estimat…
alfaro96 Jul 5, 2020
108a661
CLN Apply suggested changes
alfaro96 Jul 5, 2020
dc18f3c
MNT Minor changes
alfaro96 Jul 5, 2020
ebf97c1
MNT Minor changes
alfaro96 Jul 5, 2020
3470801
CLN Apply suggested changes
alfaro96 Jul 6, 2020
e1ee071
CLN Apply suggested changes
alfaro96 Jul 6, 2020
7f0b023
Fix Merge conflicts
alfaro96 Jul 16, 2020
b9d0015
FIX Add BaseEstimator to MyClassifier
alfaro96 Jul 16, 2020
b36509a
FIX Implement _get_tags method in MockClassifier
alfaro96 Jul 17, 2020
4a3176c
Merge remote-tracking branch 'upstream/master' into deprecate_estimat…
alfaro96 Jul 17, 2020
78b8655
CLN Apply suggested changes
alfaro96 Jul 17, 2020
3c55f67
FIX Fix linting issues
alfaro96 Jul 17, 2020
03fbd61
MNT Use shorten links
alfaro96 Jul 17, 2020
abb69bd
FIX Fix remaining test issues
alfaro96 Jul 17, 2020
3b5a3f8
MNT Minor changes
alfaro96 Jul 17, 2020
a1aa05c
MNT Deprecate _estimator_type private property
alfaro96 Aug 13, 2020
2269a30
FIX Minor changes
alfaro96 Aug 13, 2020
67ed0dc
FIX Minor changes
alfaro96 Aug 13, 2020
1f24a71
FIX Minor changes
alfaro96 Aug 17, 2020
04eed2e
MNT Iter
alfaro96 Oct 8, 2020
1e25e50
MNT Solve merge conflicts
alfaro96 Oct 8, 2020
e386624
FIX Add @property to biclusters_
alfaro96 Oct 8, 2020
ffa6756
FIX Add unwanted removed lines
alfaro96 Oct 8, 2020
2ecf9f9
FIX Iter
alfaro96 Oct 8, 2020
67a5332
FIX Iter
alfaro96 Oct 8, 2020
8f94407
FIX Fix warning
alfaro96 Oct 8, 2020
a9acf2c
FIX Remove print statement
alfaro96 Oct 8, 2020
36b0bcd
FIX Fix tests
alfaro96 Oct 8, 2020
41e1f96
CLN Apply suggested changes
alfaro96 Oct 13, 2020
5ffb315
Merge branch 'master' into deprecate_estimator_type
alfaro96 Oct 13, 2020
5aace6c
FIX Fix linting issue
alfaro96 Oct 13, 2020
49859bf
FIX Fix tests
alfaro96 Oct 13, 2020
e26433a
FIX Fix tests
alfaro96 Oct 13, 2020
ea96ef7
Merge remote-tracking branch 'upstream/master' into deprecate_estimat…
alfaro96 Oct 14, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions doc/developers/develop.rst
Original file line number Diff line number Diff line change
Expand Up @@ -458,14 +458,19 @@ For example, cross-validation in :class:`model_selection.GridSearchCV` and
on a classifier, but not otherwise. Similarly, scorers for average precision
that take a continuous prediction need to call ``decision_function`` for classifiers,
but ``predict`` for regressors. This distinction between classifiers and regressors
is implemented using the ``_estimator_type`` attribute, which takes a string value.
is implemented using the ``estimator_type`` tag, which takes a string value.
It should be ``"classifier"`` for classifiers and ``"regressor"`` for
regressors and ``"clusterer"`` for clustering methods, to work as expected.
Inheriting from ``ClassifierMixin``, ``RegressorMixin`` or ``ClusterMixin``
will set the attribute automatically. When a meta-estimator needs to distinguish
among estimator types, instead of checking ``_estimator_type`` directly, helpers
among estimator types, instead of checking ``estimator_type`` tag, helpers
like :func:`base.is_classifier` should be used.

.. deprecated:: 0.24

The `_estimator_type` attribute is deprecated in 0.24. From 0.26
onward, the `estimator_type` estimator tag must be used instead.

Specific models
---------------

Expand Down Expand Up @@ -527,6 +532,21 @@ binary_only (default=False)
whether estimator supports binary classification but lacks multi-class
classification support.

estimator_type (default=None)
string-valued identifier of an estimator as being a
classifier, regressor, etc. It is set by mixins such
as :class:`~base.ClassifierMixin`, but needs to be
more explicitly adopted on a :term:`meta-estimator`.
Its value should usually be checked by way of a
helper such as :func:`base.is_classifier`. The
available estimator types are:

- classifier (e.g. :class:`~tree.DecisionTreeClassifier`)
- regressor (e.g. :class:`~tree.DecisionTreeRegressor`)
- clusterer (e.g. :class:`~cluster.KMeans`)
- density_estimator (e.g. :class:`~mixture.BayesianGaussianMixture`)
- outlier_detector (e.g. :class:`~ensemble.IsolationForest`)

multilabel (default=False)
whether the estimator supports multilabel output

Expand Down
21 changes: 13 additions & 8 deletions doc/glossary.rst
Original file line number Diff line number Diff line change
Expand Up @@ -356,12 +356,17 @@ General Concepts

.. glossary::

``_estimator_type``
This string-valued attribute identifies an estimator as being a
classifier, regressor, etc. It is set by mixins such as
:class:`base.ClassifierMixin`, but needs to be more explicitly
adopted on a :term:`meta-estimator`. Its value should usually be
checked by way of a helper such as :func:`base.is_classifier`.
``_estimator_type``
This string-valued attribute identifies an estimator as being a
classifier, regressor, etc. It is set by mixins such as
:class:`base.ClassifierMixin`, but needs to be more explicitly
adopted on a :term:`meta-estimator`. Its value should usually be
checked by way of a helper such as :func:`base.is_classifier`.

.. deprecated:: 0.24

The `_estimator_type` attribute is deprecated in 0.24. From 0.26
onward, the `estimator_type` estimator tag must be used instead.

``_pairwise``
This boolean attribute indicates whether the data (``X``) passed to
Expand Down Expand Up @@ -804,7 +809,7 @@ Class APIs and Estimator Types

Classifiers must store a :term:`classes_` attribute after fitting,
and usually inherit from :class:`base.ClassifierMixin`, which sets
their :term:`_estimator_type` attribute.
their :ref:`estimator_type <estimator_tags>` tag.

A classifier can be distinguished from other estimators with
:func:`~base.is_classifier`.
Expand Down Expand Up @@ -942,7 +947,7 @@ Class APIs and Estimator Types
with :term:`continuous` output values.

Regressors usually inherit from :class:`base.RegressorMixin`, which
sets their :term:`_estimator_type` attribute.
sets their :ref:`estimator_type <estimator_tags>` tag.

A regressor can be distinguished from other estimators with
:func:`~base.is_regressor`.
Expand Down
102 changes: 91 additions & 11 deletions sklearn/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from . import __version__
from ._config import get_config
from .utils import _IS_32BIT
from .utils.deprecation import deprecated
from .utils.validation import check_X_y
from .utils.validation import check_array
from .utils._estimator_html_repr import estimator_html_repr
Expand All @@ -39,6 +40,7 @@
'preserves_dtype': [np.float64],
'requires_y': False,
'pairwise': False,
'estimator_type': None
}


Expand Down Expand Up @@ -488,7 +490,13 @@ def _repr_mimebundle_(self, **kwargs):
class ClassifierMixin:
"""Mixin class for all classifiers in scikit-learn."""

_estimator_type = "classifier"
# TODO: Remove in 0.26
# mypy error: Decorated property not supported
@deprecated("Attribute _estimator_type was deprecated in " # type: ignore
"version 0.24 and will be removed in 0.26.")
@property
def _estimator_type(self):
return "classifier"

def score(self, X, y, sample_weight=None):
"""
Expand Down Expand Up @@ -518,12 +526,19 @@ def score(self, X, y, sample_weight=None):
return accuracy_score(y, self.predict(X), sample_weight=sample_weight)

def _more_tags(self):
return {'requires_y': True}
return {'requires_y': True, 'estimator_type': 'classifier'}


class RegressorMixin:
"""Mixin class for all regression estimators in scikit-learn."""
_estimator_type = "regressor"

# TODO: Remove in 0.26
# mypy error: Decorated property not supported
@deprecated("Attribute _estimator_type was deprecated in " # type: ignore
"version 0.24 and will be removed in 0.26.")
@property
def _estimator_type(self):
return "regressor"

def score(self, X, y, sample_weight=None):
"""Return the coefficient of determination :math:`R^2` of the
Expand Down Expand Up @@ -572,12 +587,19 @@ def score(self, X, y, sample_weight=None):
return r2_score(y, y_pred, sample_weight=sample_weight)

def _more_tags(self):
return {'requires_y': True}
return {'requires_y': True, 'estimator_type': 'regressor'}


class ClusterMixin:
"""Mixin class for all cluster estimators in scikit-learn."""
_estimator_type = "clusterer"

# TODO: Remove in 0.26
# mypy error: Decorated property not supported
@deprecated("Attribute _estimator_type was deprecated in " # type: ignore
"version 0.24 and will be removed in 0.26.")
@property
def _estimator_type(self):
return "clusterer"

def fit_predict(self, X, y=None):
"""
Expand All @@ -602,7 +624,7 @@ def fit_predict(self, X, y=None):
return self.labels_

def _more_tags(self):
return {"preserves_dtype": []}
return {"preserves_dtype": [], "estimator_type": "clusterer"}


class BiclusterMixin:
Expand Down Expand Up @@ -722,7 +744,14 @@ def fit_transform(self, X, y=None, **fit_params):

class DensityMixin:
"""Mixin class for all density estimators in scikit-learn."""
_estimator_type = "DensityEstimator"

# TODO: Remove in 0.26
# mypy error: Decorated property not supported
@deprecated("Attribute _estimator_type was deprecated in " # type: ignore
"version 0.24 and will be removed in 0.26.")
@property
def _estimator_type(self):
return "density_estimator"

def score(self, X, y=None):
"""Return the score of the model on the data `X`.
Expand All @@ -741,10 +770,20 @@ def score(self, X, y=None):
"""
pass

def _more_tags(self):
return {'estimator_type': 'density_estimator'}


class OutlierMixin:
"""Mixin class for all outlier detection estimators in scikit-learn."""
_estimator_type = "outlier_detector"

# TODO: Remove in 0.26
# mypy error: Decorated property not supported
@deprecated("Attribute _estimator_type was deprecated in " # type: ignore
"version 0.24 and will be removed in 0.26.")
@property
def _estimator_type(self):
return "outlier_detector"

def fit_predict(self, X, y=None):
"""Perform fit on X and returns labels for X.
Expand All @@ -767,6 +806,9 @@ def fit_predict(self, X, y=None):
# override for transductive outlier detectors like LocalOulierFactor
return self.fit(X).predict(X)

def _more_tags(self):
return {'estimator_type': 'outlier_detector'}


class MetaEstimatorMixin:
_required_parameters = ["estimator"]
Expand Down Expand Up @@ -799,7 +841,7 @@ def is_classifier(estimator):
out : bool
True if estimator is a classifier and False otherwise.
"""
return getattr(estimator, "_estimator_type", None) == "classifier"
return _is_estimator_type(estimator, "classifier")


def is_regressor(estimator):
Expand All @@ -815,7 +857,7 @@ def is_regressor(estimator):
out : bool
True if estimator is a regressor and False otherwise.
"""
return getattr(estimator, "_estimator_type", None) == "regressor"
return _is_estimator_type(estimator, "regressor")


def is_outlier_detector(estimator):
Expand All @@ -831,7 +873,45 @@ def is_outlier_detector(estimator):
out : bool
True if estimator is an outlier detector and False otherwise.
"""
return getattr(estimator, "_estimator_type", None) == "outlier_detector"
return _is_estimator_type(estimator, "outlier_detector")


def _is_estimator_type(estimator, estimator_type):
"""Returns True if the estimator is of the given type.

Parameters
----------
estimator : object
Estimator object to test.

estimator_type : str
Estimator type to test.

Returns
-------
out : bool
True if `estimator` is of the given type and False otherwise.
"""
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=FutureWarning)
has_estimator_type_attribute = hasattr(estimator, "_estimator_type")
estimator_type_attribute = getattr(estimator, "_estimator_type", None)

if hasattr(estimator, "_get_tags") and callable(estimator._get_tags):
estimator_type_tag = estimator._get_tags().get("estimator_type", None)
else:
estimator_type_tag = None

if has_estimator_type_attribute:
if estimator_type_attribute != estimator_type_tag:
warnings.warn("_estimator_type attribute was deprecated in 0.24 "
"and will be removed in 0.26. Set the estimator "
"tags of your estimator instead.", FutureWarning)

return estimator_type_attribute == estimator_type

# Use the estimator_type tag when the attribute is not present
return estimator_type_tag == estimator_type


def _is_pairwise(estimator):
Expand Down
7 changes: 6 additions & 1 deletion sklearn/feature_selection/_rfe.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import numbers
from joblib import Parallel, effective_n_jobs

from ..utils.deprecation import deprecated
from ..utils.metaestimators import if_delegate_has_method
from ..utils.metaestimators import _safe_split
from ..utils.validation import check_is_fitted
Expand Down Expand Up @@ -159,6 +160,10 @@ def __init__(self, estimator, *, n_features_to_select=None, step=1,
self.importance_getter = importance_getter
self.verbose = verbose

# TODO: Remove in 0.26
# mypy error: Decorated property not supported
@deprecated("Attribute _estimator_type was deprecated in " # type: ignore
"version 0.24 and will be removed in 0.26.")
@property
def _estimator_type(self):
return self.estimator._estimator_type
Expand Down Expand Up @@ -375,7 +380,7 @@ def _more_tags(self):
return {'poor_score': True,
'allow_nan': estimator_tags.get('allow_nan', True),
'requires_y': True,
}
'estimator_type': estimator_tags.get('estimator_type', None)}


class RFECV(RFE):
Expand Down
15 changes: 13 additions & 2 deletions sklearn/feature_selection/tests/test_rfe.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def set_params(self, **params):
return self

def _get_tags(self):
return {}
return {'estimator_type': 'classifier'}


def test_rfe_features_importance():
Expand Down Expand Up @@ -282,7 +282,6 @@ def test_rfecv_grid_scores_size():

def test_rfe_estimator_tags():
rfe = RFE(SVC(kernel='linear'))
assert rfe._estimator_type == "classifier"
# make sure that cross-validation is stratified
iris = load_iris()
score = cross_val_score(rfe, iris.data, iris.target)
Expand Down Expand Up @@ -491,3 +490,15 @@ def test_multioutput(ClsRFE):
clf = RandomForestClassifier(n_estimators=5)
rfe_test = ClsRFE(clf)
rfe_test.fit(X, y)


# TODO: Remove in 0.26 when the _estimator_type attribute is removed
def test_rfe_estimator_type_deprecated():
# Assert that the _estimator_type attribute is deprecated
rfe = RFE(SVC())

msg = ("Attribute _estimator_type was deprecated in "
"version 0.24 and will be removed in 0.26.")

with pytest.warns(FutureWarning, match=msg):
assert rfe._estimator_type == "classifier"
3 changes: 2 additions & 1 deletion sklearn/metrics/_plot/tests/test_plot_curve_common.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest

from sklearn.base import BaseEstimator
from sklearn.base import ClassifierMixin
from sklearn.base import clone
from sklearn.compose import make_column_transformer
Expand Down Expand Up @@ -53,7 +54,7 @@ def test_plot_curve_error_no_response(
):
X, y = data_binary

class MyClassifier(ClassifierMixin):
class MyClassifier(ClassifierMixin, BaseEstimator):
def fit(self, X, y):
self.classes_ = [0, 1]
return self
Expand Down
7 changes: 6 additions & 1 deletion sklearn/metrics/tests/test_score_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,11 +624,16 @@ def test_multimetric_scorer_calls_method_once(scorers, expected_predict_count,
predict_proba_func = Mock(return_value=proba)
decision_function_func = Mock(return_value=pos_proba)

get_tags_func = Mock(return_value=dict(estimator_type="classifier"))

mock_est.fit = fit_func
mock_est.predict = predict_func
mock_est.predict_proba = predict_proba_func
mock_est.decision_function = decision_function_func
# add the classes that would be found during fit
mock_est._estimator_type = "classifier"
mock_est._get_tags = get_tags_func

# Add the classes that would be found during fit
mock_est.classes_ = np.array([0, 1])

scorer_dict = _check_multimetric_scoring(LogisticRegression(), scorers)
Expand Down
Loading