Skip to content

Migrate WoEEncoder to narwhals, add polars support - #1032

Merged
solegalli merged 7 commits into
narwhals-migrationfrom
narwhals-woe-encoder
Sep 15, 2026
Merged

solegalli merged 7 commits into
narwhals-migrationfrom
narwhals-woe-encoder

Conversation

@solegalli

@solegalli solegalli commented Aug 30, 2026 •

Copy link
Copy Markdown
Collaborator

Depends on #1047 (shared encoder helper and init checks).

Migrates WoEEncoder to narwhals with polars support.

fit() splits by backend: pandas keeps _calculate_woe()'s existing two-groupby implementation unchanged (it's directly unit-tested for that exact pandas-Series-with-category-index contract); polars/other narwhals backends use one group_by(), deriving the negative-class count as the complement of the positive-class count per category — benchmarked competitive with, and often faster than, pandas-native at 50k–100k rows. Zero-count-per-class fill_value handling preserved exactly.

Bug fix: _check_fit_input() assumed y was always a pandas Series (y.nunique()/y.min()/y.max()), breaking on a numpy y (list/array-like target, which sklearn's check_X_y converts via column_or_1d). Wrapped numpy y into a narwhals Series aligned to X's backend; for pandas, also aligned it to X's actual index, since _calculate_woe()'s y.groupby(X[var]) aligns by index and a mismatched default RangeIndex silently drops every row instead of raising, leaving encoder_dict_ empty. Fixes test_encoders_when_x_pandas_y_numpy's WoEEncoder case (was failing on the unmigrated file — confirmed pre-existing).

Verified: 44/44 own tests; full encoding suite 342 passed / 16 failed (was 17 pre-existing on the narwhals-encoding-base baseline — one fewer here since this branch's own numpy-y bug is now fixed, rest confirmed unrelated). flake8 / mypy clean, sphinx -W clean.

@solegalli

Copy link
Copy Markdown
Collaborator Author

Updated this branch:

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

solegalli and others added 5 commits September 15, 2026 13:59
fit() splits by backend: pandas keeps _calculate_woe()'s existing
two-groupby implementation unchanged (it's directly unit-tested for that
exact pandas-Series-with-category-index contract); polars/other narwhals
backends use one group_by() instead of two, deriving the negative-class
count as the complement of the positive-class count per category -
benchmarked competitive with, and often faster than, pandas-native at
50k-100k rows. Zero-count-per-class fill_value handling preserved exactly.

Bug fix: _check_fit_input() previously assumed y was always a pandas
Series (y.nunique()/y.min()/y.max()), breaking on a numpy y (e.g. a plain
list/array-like target, which sklearn's check_X_y machinery converts via
column_or_1d). Wrapped numpy y into a narwhals Series aligned to X's
backend; for pandas specifically, also had to line the wrapped Series up
with X's actual index, since _calculate_woe()'s y.groupby(X[var]) aligns
by index and a mismatched default RangeIndex silently drops every row
instead of raising, leaving encoder_dict_ empty. Fixes
test_encoders_when_x_pandas_y_numpy's WoEEncoder case (was failing on the
unmigrated file, confirmed pre-existing).

Verified: 44/44 own tests, full encoding suite 342 passed/16 failed (was
17 pre-existing on the narwhals-encoding-base baseline - one less here
since this branch's own numpy-y bug is now fixed, rest confirmed
unrelated), flake8 and mypy clean, sphinx -W build clean (only the
pre-existing unrelated linkcode_resolve warning).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
check_X_y now returns a narwhals frame. In _check_fit_input, bind that to
nw_X and keep the original native X: the nwd.is_pandas_dataframe(X) check,
the native_y.index = X.index alignment and the returned X all need native
input, and fit()'s pandas _calculate_woe fast path and nwd checks are then
unchanged (X stays native so no rehydration is needed). Take the y-series
backend from nw_X.implementation instead of re-wrapping X. 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>
Replace the file-local data dicts and assert_df_equal/_none_to_nan 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>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
solegalli and others added 2 commits September 15, 2026 14:24
… SelectByInformationValue

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ith_zero_counts_

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@solegalli
solegalli merged commit 9e4e99b into narwhals-migration Sep 15, 2026
4 of 10 checks passed
@solegalli
solegalli deleted the narwhals-woe-encoder branch September 15, 2026 13:05
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