MAINT Uses available_if in multiclass instead of if_delegate_has_method
@@ -55,7 +55,7 @@ from .utils.multiclass import (
check_classification_targets,
_ovr_decision_function,
)
-from .utils.metaestimators import _safe_split, if_delegate_has_method
+from .utils.metaestimators import _safe_split, available_if
from .utils.fixes import delayed
from joblib import Parallel
@@ -165,6 +165,18 @@ class _ConstantPredictor(BaseEstimator):
return np.repeat([np.hstack([1 - self.y_, self.y_])], _num_samples(X), axis=0)
+def _estimators_has(attr):
+ """Check if self.estimator or self.estimators_[0] has attr.
+
+ If `self.estimators_[0]` has the attr, then its safe to assume that other
+ values has it too. This function is used together with `avaliable_if`.
+ """
+ return lambda self: (
+ hasattr(self.estimator, attr)
+ or (hasattr(self, "estimators_") and hasattr(self.estimators_[0], attr))
+ )
+
+
class OneVsRestClassifier(
MultiOutputMixin, ClassifierMixin, MetaEstimatorMixin, BaseEstimator
):
@@ -333,7 +345,7 @@ class OneVsRestClassifier(
return self
- @if_delegate_has_method("estimator")
+ @available_if(_estimators_has("partial_fit"))
def partial_fit(self, X, y, classes=None):
"""Partially fit underlying estimators
@@ -440,7 +452,7 @@ class OneVsRestClassifier(
)
return self.label_binarizer_.inverse_transform(indicator)
- @if_delegate_has_method(["_first_estimator", "estimator"])
+ @available_if(_estimators_has("predict_proba"))
def predict_proba(self, X):
"""Probability estimates.
@@ -479,7 +491,7 @@ class OneVsRestClassifier(
Y /= np.sum(Y, axis=1)[:, np.newaxis]
return Y
- @if_delegate_has_method(["_first_estimator", "estimator"])
+ @available_if(_estimators_has("decision_function"))
def decision_function(self, X):
"""Returns the distance of each sample from the decision boundary for
each class. This can only be used with estimators which implement the
@@ -564,10 +576,6 @@ class OneVsRestClassifier(
"""Indicate if wrapped estimator is using a precomputed Gram matrix"""
return {"pairwise": _safe_tags(self.estimator, key="pairwise")}
- @property
- def _first_estimator(self):
- return self.estimators_[0]
-
def _fit_ovo_binary(estimator, X, y, i, j):
"""Fit a single binary estimator (one-vs-one)."""
@@ -726,7 +734,7 @@ class OneVsOneClassifier(MetaEstimatorMixin, ClassifierMixin, BaseEstimator):
return self
- @if_delegate_has_method(delegate="estimator")
+ @available_if(_estimators_has("partial_fit"))
def partial_fit(self, X, y, classes=None):
"""Partially fit underlying estimators
