Skip to content

Migrate DecisionTreeEncoder to narwhals, add polars support - #1026

Merged
solegalli merged 8 commits into
narwhals-migrationfrom
narwhals-decision-tree-encoder
Sep 18, 2026
Merged

solegalli merged 8 commits into
narwhals-migrationfrom
narwhals-decision-tree-encoder

Conversation

@solegalli

@solegalli solegalli commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

Depends on #1058 (integer column names in the discretisers). Until #1058 is merged, this PR also shows its files.

Migrates DecisionTreeEncoder to narwhals, with polars support.

  • fit uses the same pipeline as before the migration: OrdinalEncoder (encoding_method, ignore_format) followed by DecisionTreeDiscretiser (cv, scoring, param_grid, regression, random_state, n_jobs), both already migrated. The category-to-prediction mapping is read from the unique category and prediction pairs of the pipeline output, rounded with precision.
  • New n_jobs parameter, passed to DecisionTreeDiscretiser to fit the trees in parallel.
  • Tests follow the shared test structure, including a list/array target test and a pandas-only integer column name test.

encoder_dict_ is identical to the previous hand-written implementation in 37 cases (pandas and polars, both encoding methods, regression and classification, precision None/0/2, list target, numeric variables, n_jobs=2, integer column names). The pipeline is about 10% slower on pandas and 2% slower on polars than that implementation, because it transforms and predicts every row; most of the time is the grid search in both.

@solegalli

Copy link
Copy Markdown
Collaborator Author

Updated this branch:

Locally: test_decision_tree_encoder.py 60 passed; tests/test_encoding has no failures beyond those already on narwhals-migration.

@solegalli

Copy link
Copy Markdown
Collaborator Author

waiting for #1042

@solegalli
solegalli force-pushed the narwhals-decision-tree-encoder branch 2 times, most recently from 4c6f6ac to c69c37a Compare September 18, 2026 12:56
solegalli and others added 8 commits September 18, 2026 14:57
Replaces the old sklearn Pipeline(OrdinalEncoder, DecisionTreeDiscretiser)
composition with a direct narwhals-based fit: each variable's categories
are ordinal-encoded via a dict built from either a target-mean group_by
(encoding_method="ordered") or plain unique-value enumeration
("arbitrary"), a decision tree is trained on the ordinal codes, and
predictions are made only on the (few) unique codes rather than the full
column, since the tree's output for a category depends only on its code -
identical result, far less prediction work for a low-cardinality variable.

The "ordered" path sorts by (mean, category) rather than mean alone,
matching the tie-break fix applied to the sibling OrdinalEncoder/
MeanEncoder migrations this session, since group_by's own row order isn't
guaranteed to match across backends for tied means.

Added n_jobs (default None, sequential, unchanged behavior), parallelizing
tree training across variables via joblib threads, following the same
pattern as DecisionTreeFeatures/DecisionTreeDiscretiser.

Verified: 56/56 own tests, full encoding suite 345 passed/17 pre-existing
failures (matches the narwhals-encoding-base baseline exactly), flake8
and mypy clean, sphinx -W build clean (only the pre-existing unrelated
linkcode_resolve warning), no pandas import in this file itself (the
package-level import chain still needs pandas only because sibling
encoders on this branch aren't migrated yet, expected given the
per-encoder parallel-branch strategy).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
check_X_y now returns a narwhals frame, so bind that to nw_X and keep the
original native X for _check_or_select_variables, _check_contains_na and
_get_feature_names_in (those helpers still expect native input, matching
the CategoricalImputer migration on narwhals-migration). Drop the
redundant nw.from_native(X) in fit(); the parallel _fit_one_variable
calls reuse nw_X from check_X_y. In transform(), bind
_check_transform_input_and_state to nw_X, keep native X for
_check_contains_na, and pass nw_X to _encode (which now expects narwhals).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ests

Replace the file-local _to_backend/_assert_values helpers with the shared
test structure: make_df and data_enc* fixtures, y built with make_series on
the backend under test, isinstance(X, make_df) plus to_dict() checks, and
pytest.raises/warns(match=re.escape(msg)). Add a test passing the target as a
list and as a numpy array, which take a different code path than a Series.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e, tidy tests

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@solegalli
solegalli force-pushed the narwhals-decision-tree-encoder branch from c69c37a to 10e728b Compare September 18, 2026 12:58
@solegalli
solegalli merged commit 7df586c into narwhals-migration Sep 18, 2026
4 of 10 checks passed
@solegalli
solegalli deleted the narwhals-decision-tree-encoder branch September 18, 2026 13:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant