Repository navigation
RepresentationSearchCV lacks predict_proba, classes_ and classifier tags, so nested roc_auc CV returns NaN #92
Description
Description
RepresentationSearchCV wrapping a classifier does not behave like one. It is a plain BaseEstimator with only predict and score:
- no
classes_,predict_proba,predict_log_probaordecision_function; - its estimator type is unset, so
sklearn.base.is_classifier(search)isFalse.
As a result, cross_val_score(search, X, y, scoring="roc_auc") returns all-NaN scores, with only a UserWarning ("Scoring failed"). Outer cross-validation with an integer cv also uses plain KFold instead of StratifiedKFold. scikit-learn's search meta-estimators (GridSearchCV, RandomizedSearchCV) delegate all of these to the refitted best estimator.
Affected code
pretab/compose/search.py:34:class RepresentationSearchCV(BaseEstimator).pretab/compose/search.py:123-134: onlypredictandscoreare delegated.
Reproduction
import warnings
import numpy as np, pandas as pd
from sklearn.base import is_classifier
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV, cross_val_score
from pretab import RepresentationSearchCV
rng = np.random.default_rng(0)
X = pd.DataFrame({"x": rng.uniform(-3, 3, 300)})
y = (np.sin(X["x"]) + rng.normal(0, 0.3, 300) > 0).astype(int)
search = RepresentationSearchCV(LogisticRegression(), ["minmax", "ple"], cv=3).fit(X, y)
grid = GridSearchCV(LogisticRegression(), {"C": [0.1, 1.0]}, cv=3).fit(X, y)
for name, est in (("GridSearchCV", grid), ("RepresentationSearchCV", search)):
print(f"{name:23s} is_classifier={is_classifier(est)}, predict_proba={hasattr(est, 'predict_proba')}, "
f"classes_={getattr(est, 'classes_', None)}")
with warnings.catch_warnings():
warnings.simplefilter("ignore")
print("nested CV roc_auc:", cross_val_score(RepresentationSearchCV(LogisticRegression(), ["minmax", "ple"], cv=3),
X, y, scoring="roc_auc", cv=3))Output (PreTab 1.0.0 (6bacaba), Python 3.12, numpy 2.5.3, pandas 2.3.3, scikit-learn 1.9.1, scipy 1.18.1):
GridSearchCV is_classifier=True, predict_proba=True, classes_=[0 1]
RepresentationSearchCV is_classifier=False, predict_proba=False, classes_=None
nested CV roc_auc: [nan nan nan]
Expected behavior
The same surface as GridSearchCV: predict_proba / predict_log_proba / decision_function / classes_ delegated to best_estimator_ when the wrapped estimator has them, and the estimator type taken from the wrapped estimator.
Actual behavior
The attributes are missing, is_classifier is False, and probability-based scorers fail on every fold.
Impact
Nested evaluation of a representation search, a natural next step after using it, silently produces NaN scores for any probability-based metric. Probability predictions of the selected model are only reachable by transforming with best_preprocessor_ and calling best_estimator_.predict_proba by hand.
Root cause
The class does not delegate the classifier API and does not take its estimator type from the wrapped estimator.
Suggested fix direction
- Delegate with
sklearn.utils.metaestimators.available_if, asBaseSearchCVdoes. - Expose
classes_after fit. - Derive the estimator type in
__sklearn_tags__fromself.estimator.
Related issues
- RepresentationSearchCV with a classifier uses task="regression" for placement; string labels crash #77:
RepresentationSearchCVwith a classifier also never passes the classification task to itsPreprocessors. - RepresentationSearchCV cannot use group-based CV splitters and accepts no groups or fit parameters #91: another part of the scikit-learn search API that
RepresentationSearchCVdoes not provide (groupsand fit parameters).
Activity
[-]RepresentationSearchCV around a classifier exposes no predict_proba / classes_ and is not a classifier, so nested CV with roc_auc returns NaN[/-][+]RepresentationSearchCV lacks predict_proba, classes_ and classifier tags, so nested roc_auc CV returns NaN[/+]