Fix GapEncoder(init="k-means") crash on columns containing nulls - #2238
Open
Hrafz wants to merge 1 commit into
Open
Fix GapEncoder(init="k-means") crash on columns containing nulls#2238Hrafz wants to merge 1 commit into
Hrafz wants to merge 1 commit into
Conversation
GapEncoder(init="k-means") crashed on any column containing a missing value, while init="k-means++" and init="random" fit the same column without error. _init_vars builds the null-substituted array unq_X via unique_strings(X, is_null) and uses it everywhere else in the method, but was calling self._init_w(unq_V[lookup], X) with the raw X (still containing None/NaN). init="k-means" is the only branch that touches X directly: it re-vectorizes it from scratch inside get_kmeans_prototypes, which is where the null reaches scikit-learn's text preprocessor and crashes. "k-means++" and "random" only ever consume the already- vectorized V, so they are unaffected. The exact exception depends on which null sentinel is hit first (AttributeError for None/pd.NA, ValueError for np.nan). Fix: pass unq_X[lookup] instead of X, the same per-sample reconstruction already used throughout the method. When there are no nulls, unq_X[lookup] == X, so this is a no-op for the common case. test_missing_values is now parametrized over init, so it also exercises "k-means" (previously only the default "k-means++"). Before the fix: 2/6 parametrizations fail with the errors above; after: all 6 pass.
Hrafz
force-pushed
the
fix/gap-encoder-kmeans-init-null-crash
branch
from
August 1, 2026 18:10
4afe1a8 to
a994e9c
Compare
Member
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.
Addresses issue
#2237
Description
GapEncoder(init="k-means")crashes on any column containing a missingvalue, while the two other
initoptions fit the same column fine:Expected:
init="k-means"handles the column's nulls the same way the othertwo
initoptions do (nulls are treated as the empty string throughoutGapEncoder, seeskrub._utils.unique_stringsand the existingtest_missing_values).Actual: it raises from scikit-learn's text preprocessor. The exception type
depends on the column's dtype rather than on which sentinel appears first:
init="k-means"raisesnp.nanValueError: np.nan is an invalid document, expected byte or unicode string.NoneValueError: np.nan is an invalid document, expected byte or unicode string.stringdtype containingpd.NAAttributeError: 'NAType' object has no attribute 'lower'init="k-means++"andinit="random"fit all three without error.Cause:
_init_varsbuilds the null-substituted arrayunq_Xviaunique_strings(X, is_null)and uses it everywhere else in the method, butwas calling
self._init_w(unq_V[lookup], X)with the rawX(stillcontaining the original null). Only the
"k-means"branch touchesXdirectly — it re-vectorizes it from scratch inside
get_kmeans_prototypes,which is where the null reaches the preprocessor.
"k-means++"and"random"only ever consume the already-vectorizedV.Fix: pass
unq_X[lookup]instead ofX— the same null-substituted,per-sample reconstruction already used throughout the rest of the method.
When there are no nulls,
unq_X[lookup] == X, so this is a no-op for thecommon case.
Checklist
How Has This Been Tested?
test_missing_valuesis now parametrized overinit, so it also exercises"k-means"(previously only the default"k-means++").Before the fix (test change only, source untouched):
After the fix:
The full
skrub/testssuite passes with no failures and no change in theskipped/xfailed/xpassed counts beyond the newly-passing parametrizations.