Skip to content

FIX KMeansSMOTE: uniform weights for degenerate clusters (#1186) - #1187

Open
chaoz23 wants to merge 1 commit into
scikit-learn-contrib:masterfrom
chaoz23:fix/kmeans-smote-degenerate-clusters
Open

FIX KMeansSMOTE: uniform weights for degenerate clusters (#1186)#1187
chaoz23 wants to merge 1 commit into
scikit-learn-contrib:masterfrom
chaoz23:fix/kmeans-smote-degenerate-clusters

Conversation

@chaoz23

@chaoz23 chaoz23 commented Jul 18, 2026

Copy link
Copy Markdown

Toward #1186 — fixes the crash (issue 1). The exact-count allocation (issue 2) is intentionally left as a follow-up, so this does not auto-close the issue.

What

When every sample within each valid cluster is identical, all cluster sparsities are 0, so cluster_sparsities / cluster_sparsities.sum() divides by zero and yields NaN weights. The subsequent math.ceil(...) then raises ValueError: cannot convert float NaN to integer.

This can happen with duplicated rows or discrete/one-hot features where a whole cluster collapses to a single point.

Change

  • Weight such degenerate clusters uniformly when the total sparsity is 0, instead of dividing by zero.
  • Move the existing empty-valid_clusters RuntimeError guard ahead of the weight computation (previously it ran an unnecessary division on an empty array first).
  • Add a non-regression test reproducing the reported case.

Reproducer (from the issue)

import numpy as np
from imblearn.over_sampling import KMeansSMOTE
from sklearn.cluster import KMeans

X = np.array([[1., 1.]]*4 + [[2., 2.]]*4 + [[3., 3.]]*4)
y = np.array([1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0])
KMeansSMOTE(
    kmeans_estimator=KMeans(n_clusters=2, random_state=42),
    random_state=42, cluster_balance_threshold=0.1,
).fit_resample(X, y)   # was: ValueError; now: resamples cleanly

Scope

Limited to issue 1 (the crash). Issue 2 (making the total number of generated samples exactly match the requested count) is a separate behavioral change — happy to follow up on it in its own PR.

@chaoz23
chaoz23 force-pushed the fix/kmeans-smote-degenerate-clusters branch from e295a4e to a5f6f4d Compare July 18, 2026 04:30
When every sample within each valid cluster is identical, all cluster
sparsities are 0, so dividing by their (zero) sum produced NaN weights
and crashed with 'ValueError: cannot convert float NaN to integer' in
math.ceil. Fall back to uniform weighting across the valid clusters.

Toward scikit-learn-contrib#1186: fixes the crash (issue 1). The exact-count allocation
(issue 2) is left as a follow-up, so this does not auto-close the issue.
@chaoz23
chaoz23 force-pushed the fix/kmeans-smote-degenerate-clusters branch from a5f6f4d to 829f723 Compare July 18, 2026 05:13
@chaoz23

chaoz23 commented Jul 31, 2026

Copy link
Copy Markdown
Author

Gentle ping on this one — CI is green and it's been a couple of weeks. No rush if you're busy, just flagging in case it slipped past.

One disclosure while I'm here: I used an AI coding assistant while working on this patch, which scikit-learn's automated contributions policy asks contributors to state. I read #1186 in full, reviewed and tested every line myself, and can explain any of it on request. The reproducer in the description is the one from the issue; I ran it before and after the change.

Happy to pick up issue 2 (making the generated sample count exactly match the request) in a separate PR if that's useful.

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