Skip to content

Fix uncomparable values in SimpleImputer tie-breaking strategy - #31820

Merged
betatim merged 6 commits into
scikit-learn:mainfrom
AlexandreAbraham:fix-31717
Aug 21, 2025
Merged

betatim merged 6 commits into
scikit-learn:mainfrom
AlexandreAbraham:fix-31717

Conversation

@AlexandreAbraham

Copy link
Copy Markdown
Contributor

Fixes #31717

When encountering uncomparable types, SimpleImputer's 'min' strategy previously raised an error. This commit resolves the issue by selecting the value with the smallest hash of its pickled representation as a fallback.

The pickled value is used because some special values such as None are not handled natively by murmurhash.

When encountering uncomparable types, SimpleImputer's 'min'
strategy previously raised an error. This commit resolves the
issue by selecting the value with the smallest hash of its pickled
representation as a fallback.

Fix scikit-learn#31717
@github-actions

github-actions Bot commented Jul 22, 2025 •

Copy link
Copy Markdown

✔️ Linting Passed

All linting checks passed. Your pull request is in excellent shape! ☀️

Generated for commit: a631e9c. Link to the linter CI: here

Comment thread sklearn/impute/_base.py Outdated
return min(items)
except TypeError as e:
if "'<' not supported between" in str(e):
return min(items, key=lambda x: murmurhash3_32(pickle.dumps(x)))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it's not a bit overkill. For the use case of SimpleImputer, I believe that using the string representation of objects would be fine. It's deterministic across versions, os, envs... for usual types (int, float, str, None). It's not on more complex objects but I don't think that we expect complex objects to be passed to SimpleImputer.

Suggested change
return min(items, key=lambda x: murmurhash3_32(pickle.dumps(x)))
return min(items, key=str)

What do you think ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought of that too but I am bugged by the 1 out of a million case where there is a tie between None and 'None' that is shuffle-sensitive:

In [1]: min([None, 'None'], key=str)

In [2]: min(['None', None], key=str)
Out[2]: 'None'

repr does not fail in that case but it may in others.

Actually, we do not really need the hash, the pickle dumped string should be unique.

            return min(items, key=lambda x: pickle.dumps(x))

Is that better? Still needs pickle though...
Actually, that's where the other PR comes in handy, using the type breaks the tie.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually the same for 1 and "1" and so on which is more likely to happen.

I was undecided between pickle.dumps(x) and (str(type(x), str(x)), but a quick benchmark showed that the latter is much faster, so I'm in favor of choosing this one.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe another reason to not rely on pickle is that IIRC there are very little promises about the contents of a pickle with respect to the Python version (I think you'd have to select an explicit version of the protocol?). Generally it feels like using pickle is somehow "too complicated"

@jeremiedbb jeremiedbb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More comments. Please also add a changelog entry

Comment thread sklearn/impute/_base.py Outdated
Comment thread sklearn/impute/tests/test_impute.py Outdated
Comment thread sklearn/impute/_base.py Outdated

@jeremiedbb jeremiedbb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I directly pushed a commit to use (str(type(x)), str(x)) as key. LGTM.
@betatim is this solution fine for you ?

@jeremiedbb jeremiedbb added the Waiting for Second Reviewer First reviewer is done, need a second one! label Aug 1, 2025
@jeremiedbb jeremiedbb added this to the 1.7.2 milestone Aug 1, 2025
@AlexandreAbraham

Copy link
Copy Markdown
Contributor Author

Sorry, I was off, I could not implement the requests. let me know if there is anything more I can do.

@betatim betatim left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the best compromise. I still wonder about "what could be weird edge cases" - even though I can't think of any. Let's try it and see what people report

@betatim

betatim commented Aug 21, 2025

Copy link
Copy Markdown
Member

I merged main into this branch as a way to get the CI to run again

@betatim
betatim enabled auto-merge (squash) August 21, 2025 14:39
@betatim
betatim merged commit b10b73a into scikit-learn:main Aug 21, 2025
lucyleeow pushed a commit to lucyleeow/scikit-learn that referenced this pull request Aug 22, 2025
…t-learn#31820)

Co-authored-by: Jérémie du Boisberranger <jeremie@probabl.ai>
Co-authored-by: Tim Head <betatim@gmail.com>
jeremiedbb added a commit to jeremiedbb/scikit-learn that referenced this pull request Sep 3, 2025
…t-learn#31820)

Co-authored-by: Jérémie du Boisberranger <jeremie@probabl.ai>
Co-authored-by: Tim Head <betatim@gmail.com>
@jeremiedbb jeremiedbb mentioned this pull request Sep 3, 2025
13 tasks done
jeremiedbb added a commit that referenced this pull request Sep 8, 2025
Co-authored-by: Jérémie du Boisberranger <jeremie@probabl.ai>
Co-authored-by: Tim Head <betatim@gmail.com>
DeaMariaLeon pushed a commit to DeaMariaLeon/scikit-learn that referenced this pull request Sep 12, 2025
…t-learn#31820)

Co-authored-by: Jérémie du Boisberranger <jeremie@probabl.ai>
Co-authored-by: Tim Head <betatim@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

module:impute Waiting for Second Reviewer First reviewer is done, need a second one!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SimpleImputer fails in "most_frequent" if incomparable types only if ties

3 participants