Repository navigation
Conversation
A global sklearn.set_config(transform_output="pandas") (or "polars") reached the steps of the internal ColumnTransformer. The sparse OneHotEncoder refuses pandas and polars output, so categorical_method="one-hot" and preset="expanded" failed to fit with "Pandas output does not support sparse data", and users could not work around it because the encoder's sparse_output is not exposed. The internal ColumnTransformer is now pinned to set_output(transform="default"), so its steps always produce plain arrays. The Preprocessor still wraps its stacked output in the requested container itself, so transform keeps returning a pandas or polars DataFrame under the global setting. Fixes #76
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 #76.
With scikit-learn's global output set to pandas or polars (
sklearn.set_config(transform_output="pandas")), aPreprocessorthat one-hot encodes a column (categorical_method="one-hot", a"one-hot"entry infeature_preprocessing, orpreset="expanded") failed infitwith "Pandas output does not support sparse data". A one-hotPreprocessorfitted before the setting was switched on failed the same way intransform.Root cause
Preprocessor.fitbuilt and fitted its internalColumnTransformerwithout an output configuration of its own, so the globaltransform_outputapplied to it and to every step inside it. The one-hot steps use aOneHotEncoderwith the defaultsparse_output=True, and scikit-learn refuses pandas and polars output for sparse data.sparse_outputis not exposed throughPreprocessor, so users had no way around it other than switching the global setting off.Fix
Preprocessor.fitcallsself.column_transformer_.set_output(transform="default")before fitting the internalColumnTransformer. This also pins its steps, so they always produce plain arrays and the global setting no longer reaches them. The configuration is stored on the fittedColumnTransformer, sotransformof aPreprocessorfitted before the global setting was switched on works as well.Preprocessor.transformis unchanged: it still reads the requested container from its own output configuration (set_outputor the global setting) and builds the pandas or polars frame from the stacked array itself, so it keeps returning a DataFrame under the global setting.Behaviour changes
transform_output="pandas"or"polars", one-hot encoding andpreset="expanded"now fit, andtransformreturns a float64 DataFrame with theget_feature_names_out()columns and the same values as with the default configuration.fingerprint_of a newly fittedPreprocessorchanges, because the fitted internalColumnTransformernow records its output configuration. The transformed output is unchanged, and no golden regression baseline pins a fingerprint.Documentation
docs/core_concepts/outputs_and_inspection.md("DataFrame output"): a globalsklearn.set_config(transform_output=...)is honoured likeset_outputand applies to thePreprocessor's own output only; the internal per-column steps always produce arrays, so every method, including the sparse one-hot encoding, works under it.Preprocessor.transformdocstring (Returns): the pandas / polars DataFrame is also returned under the global setting, which applies to this output only.Tests
test_one_hot_works_under_a_global_pandas_output_config[options0, options1](categorical_method="one-hot",preset="expanded"): insidesklearn.config_context(transform_output="pandas"),fitandtransformreturn a DataFrame with theget_feature_names_out()columns, all float64, and values equal to the default-configuration output. Fails onmain(raises), passes here.test_global_polars_output_config_is_honoured: the same undertransform_output="polars": a polars frame with the right column names and values (skipped when polars is not installed). Fails onmain(raises), passes here.Reproduction
Output of the script from #76 on this branch. The loop also calls
transformand prints the output type, shape and dtypes, and the script adds the two other cases from the description:transformof a one-hotPreprocessorfitted before the global setting was switched on, andtransform_output="polars".On
main, the last four lines are each "Pandas output does not support sparse data. Set sparse_output=False ..." ("Polars output ..." for the last).Checks
just test: 1441 passed, 59 skipped, 7 xfailed; coverage 93.65% (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.self.column_transformer_.fit(X, y)line inPreprocessor.fit: Preprocessor.fit crashes on any bool column under default settings #54 and Preprocessor routes integer column labels positionally, silently swapping columns #55 passself._column_transformer_input(X), output_format="auto" chooses sparse vs dense per transform batch, breaking dense-only estimators at predict #73 replaces it withfit_transformplusresolve_output_format, and Preprocessor crashes on an all-missing column at fit (numerical, one-hot) or silently drops it (int) #95 wraps it in atry/except. Whichever lands later gets a conflict there: keep this PR's comment andset_output(transform="default")line directly before whatever fit call results. output_format="auto" chooses sparse vs dense per transform batch, breaking dense-only estimators at predict #73 also appends tests at the end oftests/integration/test_output_format.py; keep both blocks. Once this PR is in, output_format="auto" chooses sparse vs dense per transform batch, breaking dense-only estimators at predict #73'snp.asarrayconversion of a dense training output is redundant but harmless.Raisessection to thePreprocessor.transformdocstring directly after theReturnsline this PR extends, and fix(output): preserve input index in set_output pandas container #68 (the PR for Preprocessor pandas output (set_output) drops the input index, breaking ColumnTransformer/FeatureUnion #60 (fix(output): preserve input index in set_output pandas container #68)) extends the same line with "(a pandas DataFrame keeps the index ofX)". Keep both texts.MissingIndicatorbranch) merges cleanly with this one. With add_missing_indicator / impute_with_indicator feeds the indicator through the scaler and basis; lineage crashes #62 alone, the float representation blocks and the boolean indicator blocks are stacked into an object-dtype DataFrame under a globaltransform_output="pandas"; with this pin as well, the output is float64. Whichever of the two PRs lands second addstest_missing_indicator_stays_numeric_under_a_global_pandas_output_configtotests/integration/test_output_format.py: under that setting,Preprocessor(numerical_method="minmax", add_missing_indicator=True)returns only float columns andnum_a__missingindicator_aequalsX["a"].isna(). It is not part of this PR because onmainthe indicator comes fromSimpleImputer(add_indicator=True), is already float, and the test passes without the fix.transform_output="polars", a pipeline passes polars frames between steps, so aPreprocessorafter another step also needs Preprocessor rejects polars DataFrames, so pipelines with set_output(transform="polars") crash #94.This change was previously part of #86, which is closed in favour of one pull request per issue.