Repository navigation
Conversation
Array input was always relabelled feature_0, feature_1, ... and routed by name, so a Preprocessor fitted on a DataFrame raised 'columns are missing' for the same data as a NumPy array (NumPy-based serving, shap.KernelExplainer), and one fitted on an array raised for a DataFrame. Following scikit-learn's convention, input of the fitted width that does not carry the fitted labels is now matched by position, with scikit-learn's usual feature-name warning. The fitted labels come from the internal ColumnTransformer, and numeric columns of an object array are re-inferred. Input matched by position with a different width raises the usual 'X has n features, but Preprocessor is expecting m features as input.' error. Fixes #78
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 #78.
A
Preprocessorfitted on a pandas DataFrame raisedcolumns are missingattransformfor the same data passed as a NumPy array of the same width, and one fitted on an array raised for a DataFrame. Pipelines containing aPreprocessortherefore failed atpredictwith NumPy-based serving,shap.KernelExplainerordf.to_numpy(), all of which scikit-learn's own transformers handle.Root cause
transformalways relabelled array inputfeature_0,feature_1, ... and then routed columns by name through the fittedColumnTransformer. Those names never match the labels of a DataFrame fit, and a DataFrame's labels never match the synthetic names of an array fit.Fix
Preprocessor.transformnow converts its input with the new privatePreprocessor._align_inputinstead ofto_dataframe:ColumnTransformer'sfeature_names_in_. Labels equal tofeature_0, ...,feature_{n-1}mark a fit on an array.X does not have valid feature names, but Preprocessor was fitted with feature names, orX has feature names, but Preprocessor was fitted without feature names). Numeric columns of an object array are re-inferred withinfer_objects, soX.to_numpy()of a mixed frame works.n_features_in_raisesPretabDataErrorwith scikit-learn's messageX has n features, but Preprocessor is expecting m features as input.This covers any array of the wrong width, which is the check the PR for Preprocessor.transform does not check n_features_in_ for ndarray input; extra columns are silently misaligned #72 (fix(preprocessor): reject NumPy input of the wrong width at transform #112) adds.Behaviour changes
UserWarning(X does not have valid feature names, ...). It raisedValueError: columns are missingbefore.UserWarning(X has feature names, ...). It raisedcolumns are missingbefore.PretabDataError(aValueError) with the scikit-learn message. Before, a narrower one raisedcolumns are missing, and a wider array after an array fit was silently misaligned (Preprocessor.transform does not check n_features_in_ for ndarray input; extra columns are silently misaligned #72).feature_0,feature_1, ... is treated like one fitted on an array. A same-width frame with other labels is then matched by position with a warning instead of raisingcolumns are missing(see the Preprocessor.get_feature_names_out(input_features) fails after fitting on an ndarray, breaking Pipeline.get_feature_names_out() #65 follow-up below).Documentation
Preprocessor.transformdocstring: theXparameter describes matching by name and by position and the two warnings; newRaisessection for positional input of the wrong width._align_inputdocstring.docs/core_concepts/configuration.md("NumPy array input"): a paragraph on a DataFrame fit with array input and an array fit with DataFrame input, the warnings, re-inference of object arrays, and the width error.Tests
test_frame_fit_accepts_an_array_of_the_fitted_width: aPreprocessor+Ridgepipeline fitted on a mixed numeric/string frame predictsX.to_numpy()(an object array) with the warning and the same values as forX. Fails onmain, passes here.test_array_fit_accepts_a_frame_of_the_fitted_width: after an array fit, a renamed frame warns and gives exactly the array's output. Fails onmain, passes here.test_positional_input_of_the_wrong_width_is_rejected: a narrower array after a frame fit and a narrower frame after an array fit both raisePretabDataErrorwith the scikit-learn message. Fails onmain, passes here.Reproduction
Output of the script from #78 on this branch, with the predictions in the
tryblock printed:Next to scikit-learn's warning for
StandardScaler, the twoPreprocessorpipelines warnX does not have valid feature names, but Preprocessor was fitted with feature namesandX has feature names, but Preprocessor was fitted without feature names.Checks
just test: 1441 passed, 59 skipped, 7 xfailed; coverage 93.68% (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.Preprocessor.transform(Preprocessor.transform does not check n_features_in_ for ndarray input; extra columns are silently misaligned #72's inline check and itsRaisestext). Take this PR's side (X = self._align_input(X)and itsRaisestext), which keeps Preprocessor.transform does not check n_features_in_ for ndarray input; extra columns are silently misaligned #72's behaviour. Preprocessor.transform does not check n_features_in_ for ndarray input; extra columns are silently misaligned #72's tests pass unchanged on the result, and the full suite passes (1444 passed, 59 skipped, 7 xfailed).docs/core_concepts/configuration.mdmerges without a conflict but then states the width error twice, so drop Preprocessor.transform does not check n_features_in_ for ndarray input; extra columns are silently misaligned #72's sentence.tests/integration/test_preprocessor.pyhas an append/append conflict: keep both blocks.transform(its_label_array_by_position, after a fit on non-string labels). It conflicts intransform, in theXdocstring and next to_align_input. Keep_align_inputand this PR's docstring, drop_label_array_by_position, and keep Preprocessor routes integer column labels positionally, silently swapping columns #55's_column_transformer_inputand_input_labels. Resolved this way, the full suite passes (1450 passed, 59 skipped, 7 xfailed), and an array after a fit onpd.DataFrame({1: [...], 0: [...]})is matched by position with the right values. Until the Preprocessor.get_feature_names_out(input_features) fails after fitting on an ndarray, breaking Pipeline.get_feature_names_out() #65 follow-up below, that case also warns that the preprocessor was fitted with feature names, which scikit-learn does not do for non-string labels. Once Preprocessor routes integer column labels positionally, silently swapping columns #55 has landed, a regression test for an integer-labelled fit with array input can be added._column_transformer_inputwhere this PR adds_align_input: keep both methods and both test blocks.Returnstext oftransformdirectly above the newRaisessection: keep both.Xdocstring oftransform(keep Preprocessor rejects polars DataFrames, so pipelines with set_output(transform="polars") crash #94's polars type and this PR's text), indocs/core_concepts/configuration.md(keep this paragraph at the end of "NumPy array input", before Preprocessor rejects polars DataFrames, so pipelines with set_output(transform="polars") crash #94's new "Polars DataFrame input" section), and at the end oftests/integration/test_preprocessor.py(keep both blocks).tests/integration/test_preprocessor.py: keep both blocks.Preprocessor.feature_names_in_. Addnot hasattr(self, "feature_names_in_")tofitted_on_arrayin_align_input, so a DataFrame whose labels are literallyfeature_0,feature_1, ... counts as a named fit. Choose the warning byhasattr(self, "feature_names_in_")as well, as in fix: resolve 10 high-priority bugs found in a library audit (#76-#85) #86, so a fit on non-string labels does not warn._align_inputdetects positional input withisinstance(X, np.ndarray). Preprocessor rejects polars DataFrames, so pipelines with set_output(transform="polars") crash #94 reads a list of rows like an array, so the check should becomenot has_column_labels(X). Otherwise a list of rows is not matched by position after a named fit, and its width is not checked after an array fit.This change was previously part of #86, which is closed in favour of one pull request per issue.