Repository navigation
Conversation
RepresentationSearchCV subclassed plain BaseEstimator and offered only predict and score, so a search over a classifier had no classes_, predict_proba, predict_log_proba or decision_function and is_classifier() was False. Nested evaluation therefore broke: cross_val_score(search, X, y, scoring="roc_auc") returned nan for every fold (with only a warning), and the outer split was a plain KFold instead of a StratifiedKFold. __sklearn_tags__ now takes the estimator type and classifier / regressor tags of the wrapped estimator, as scikit-learn's search estimators do. predict_proba, predict_log_proba and decision_function transform with best_preprocessor_ and delegate to best_estimator_; they are available only when the estimator provides them (judged from the estimator before fit and from best_estimator_ after). classes_ is exposed from best_estimator_. fit records n_features_in_ from best_preprocessor_ and, following scikit-learn, feature_names_in_ when X has string column labels. Regression usage is unchanged. Fixes #92
This was referenced Oct 16, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #92.
RepresentationSearchCVaround a classifier did not behave like a classifier: it had noclasses_,predict_proba,predict_log_probaordecision_function, andis_classifier(search)wasFalse. Nested evaluation with a probability-based scorer such ascross_val_score(search, X, y, scoring="roc_auc")scored NaN on every fold, and an outer integercvusedKFoldinstead ofStratifiedKFold.Root cause
RepresentationSearchCVis a plainBaseEstimatorthat only delegatedpredictandscoretobest_estimator_. It did not take its estimator type from the wrapped estimator, and it did not delegate the rest of the classifier API, as scikit-learn'sGridSearchCVdoes.Fix
RepresentationSearchCV.__sklearn_tags__takesestimator_type,classifier_tagsandregressor_tagsfrom the tags ofestimator.predict_proba,predict_log_probaanddecision_functiontransformXwithbest_preprocessor_and delegate tobest_estimator_. As inBaseSearchCV, they are guarded withavailable_if(_estimator_has), which checksbest_estimator_after fit and the unfittedestimatorbefore, sohasattron the search reflects the estimator it wraps.classes_is a property that returnsbest_estimator_.classes_.fitrecordsn_features_in_, andfeature_names_in_when every column label ofXis a string (scikit-learn's rule). A refit on input without string labels removes stale names.predict,scoreand the new methods share the transform through_best_representation.Behaviour changes
is_classifier(search)andis_regressor(search)follow the wrapped estimator. An outercross_val_score/cross_validatewith an integercvtherefore splits a classifier search withStratifiedKFold, so its outer folds and scores change. The search's own inner splitting is unchanged.predict_proba,predict_log_probaanddecision_function(each only when the wrapped estimator provides it) and new attributesclasses_,n_features_in_andfeature_names_in_. A search around a regressor exposes none of the delegated methods, andpredictandscoreare unchanged.Documentation
RepresentationSearchCVclass docstring: the search takes the estimator type ofestimator; theAttributessection listsclasses_,n_features_in_andfeature_names_in_. Docstrings for the delegated methods andclasses_.docs/core_concepts/target_awareness.md("Searching over representations"): new paragraph saying that the search takes the wrapped estimator's type, delegatesclasses_,predict_proba,predict_log_probaanddecision_function, and can be evaluated in an outer stratified cross-validation withscoring="roc_auc".Tests
test_search_takes_the_estimator_type: aLogisticRegressionsearch is a classifier and aRidgesearch a regressor, andcheck_cvbuilds aStratifiedKFoldfor the former. Fails onmain, passes here.test_classifier_search_delegates_to_the_best_estimator: on string labels,classes_is["neg", "pos"], andpredict_proba,predict_log_probaanddecision_functionequal those ofbest_estimator_on the best representation. Fails onmain, passes here.test_delegated_methods_follow_the_wrapped_estimator: anSVC()search exposesdecision_functionbut notpredict_proba, before and after fit; aRidgesearch exposes none of the delegated names. Fails onmain, passes here.test_unfitted_classifier_search_raises_not_fitted: before fit there is noclasses_, andpredict_probaraisesNotFittedError. Fails onmain, passes here.test_nested_cross_val_score_with_roc_auc: nestedcross_val_scorewithscoring="roc_auc"gives finite scores above 0.9. Fails onmain, passes here.test_search_records_the_input_features:n_features_in_andfeature_names_in_after a DataFrame fit; a refit on an array dropsfeature_names_in_. Fails onmain, passes here.Reproduction
Output of the script from #92 on this branch:
Checks
just test: 1444 passed, 59 skipped, 7 xfailed; coverage 93.72% (gate 90%).scripts/quickstart.pypasses.just docs: strict build succeeds.just check: all pre-commit and pre-push hooks pass (ruff format, ruff lint, prettier, file hygiene, pyright with 0 errors).Merge notes
main.tests/compose/test_search.pyafter the sameclass_datafixture, which both branches add identically; git keeps one copy. The appended tests conflict: keep both blocks.pretab/compose/search.py, keep this PR'sget_tagsandavailable_ifimports and RepresentationSearchCV cannot use group-based CV splitters and accepts no groups or fit parameters #91's_check_method_params. At the final refit infit, keep RepresentationSearchCV cannot use group-based CV splitters and accepts no groups or fit parameters #91's.fit(x_all, y_arr, **fit_params)followed by this PR'sn_features_in_/feature_names_in_lines. Indocs/core_concepts/target_awareness.md, put RepresentationSearchCV cannot use group-based CV splitters and accepts no groups or fit parameters #91's paragraphs on groups and fit parameters first and this PR's paragraph after them. Intests/compose/test_search.py, merge the two import blocks and keep both test blocks. With this branch, RepresentationSearchCV with a classifier uses task="regression" for placement; string labels crash #77's and RepresentationSearchCV cannot use group-based CV splitters and accepts no groups or fit parameters #91's combined this way, the full test suite passes (1453 passed, 59 skipped, 7 xfailed)."ple"searches on string labels in these tests passpreprocessor_params={"task": "classification"}, because onmaintarget-aware placement does not get the classification task from the search. Once RepresentationSearchCV with a classifier uses task="regression" for placement; string labels crash #77 lands this is redundant but harmless.Preprocessorrecordsfeature_names_in_itself, and the search could read it frombest_preprocessor_. It gives the same value as the rule used here.This change was previously part of #97, which is closed in favour of one pull request per issue.