Thanks to visit codestin.com
Credit goes to github.com

Skip to content

FIX Dataframe support in KNN.predict - #26772

Merged
betatim merged 8 commits into
scikit-learn:mainfrom
jeremiedbb:fix-usable-for-dataframe
Jul 10, 2023
Merged

FIX Dataframe support in KNN.predict#26772
betatim merged 8 commits into
scikit-learn:mainfrom
jeremiedbb:fix-usable-for-dataframe

Conversation

@jeremiedbb

Copy link
Copy Markdown
Member

Fixes #26768

knn.predict doesn't validate X right away because it call kneighbors that does validate X.
The issue is that ArgKMin.is_usable_for assumes that it's a numpy array. This PR simply adds a check there. Note that it is not usable for a pandas dataframe anyway because it's not c-contiguous.

@github-actions

github-actions Bot commented Jul 5, 2023

Copy link
Copy Markdown

✔️ Linting Passed

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

Generated for commit: 61467a0. Link to the linter CI: here

@jeremiedbb

Copy link
Copy Markdown
Member Author

cc/ @jjerphan

@jjerphan jjerphan 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.

LGTM. Thank you, @jeremiedbb.

@@ -96,7 +96,9 @@ def is_usable_for(cls, X, Y, metric) -> bool:
"""

def is_numpy_c_ordered(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.

Side-note: if there are any other libraries that implement the buffer protocol in Python and that we could support, we might want to adapt this.

Comment thread doc/whats_new/v1.3.rst Outdated
@jjerphan jjerphan added Quick Review For PRs that are quick to review Waiting for Second Reviewer First reviewer is done, need a second one! labels Jul 5, 2023

def is_numpy_c_ordered(X):
return hasattr(X, "flags") and X.flags.c_contiguous
if not hasattr(X, "flags"):

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 don't quite understand why this fixes it :( I always thought that something like bool(expr1 and expr2) will not evaluate expr2 if expr1 is False. From the bug report it looks like hasattr(X, "flags") will be False, but then why did it continue into evaluating X.flags.c_contiguous. And even more confusingly the exception is AttributeError: 'Flags' object has no attribute 'c_contiguous', which makes me think that hasattr(X, "flags") evaluates as True when you use a DF as input. But then there is a new test and that passes. So overall I am left confused :-/

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.

Or is the thing that actually fixes it the getattr(X.flags, "c_contiguous", False)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

which makes me think that hasattr(X, "flags") evaluates as True when you use a DF as input.

Yep, a dataframe has a flags attribute, but this flags has no c_contiguous attribute :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Or is the thing that actually fixes it the getattr(X.flags, "c_contiguous", False)?

indeed. Your understanding of the conditional evaluation of expr1 and expr2 is correct

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.

So we could have kept a one-liner then:

    return hasattr(X, "flags") and getattr(X.flags, "c_contiguous", False)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

hi every one so i have same prblm here 'AttributeError: 'Flags' object has no attribute 'c_contiguous''
and i dont understand the solution u given before . any one can help ?

jeremiedbb and others added 2 commits July 5, 2023 16:42
@jeremiedbb

Copy link
Copy Markdown
Member Author

the doc failure is probably due to #26773

@thomasjpfan thomasjpfan 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.

Thanks for the PR! Overall looks good.

Comment on lines +100 to +101
return False
return getattr(X.flags, "c_contiguous", False)

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.

Nit: (Ask for forgiveness and not permission)

def is_numpy_c_ordered(X):
    try:
        return X.flags.c_contiguous
    except AttributeError:
        return False

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm usually not a huge fan of try excepts because it hides stuff. With that it would not have crashed in the first place but it means that we would have never noticed that since we're not validating X at the beginning of predict, passing a dataframe will always take the legacy route.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

jeremiedbb i am very new to this. I have the exact same issue with c_contiguous flag.

Can you please explain what steps I must follow to fix this? I am afraid I can't quite follow what I should do to fix this issue. Please help.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This bug will be fixed in scikit-learn 1.3.1 that we plan to release soon. In the mean time you can use version 1.2 which does not have this bug.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@jeremiedbb I upgraded to scikit-learn 1.4.0 but im still getting this issue c_contiguous flag when I run knn.predict. please help!

@ogrisel

ogrisel commented Jul 6, 2023

Copy link
Copy Markdown
Member

The issue is that ArgKMin.is_usable_for assumes that it's a numpy array. This PR simply adds a check there. Note that it is not usable for a pandas dataframe anyway because it's not c-contiguous.

But shouldn't we just validate the data in predict to make it a c-contiguous array instead?

@ogrisel

ogrisel commented Jul 6, 2023

Copy link
Copy Markdown
Member

I think it's not the role of the lower level ArgKMin API to have to deal with non-numpy inputs. I think it's the estimators role to make sure that it calls its Cython backend with the proper datastructure.

@jeremiedbb

Copy link
Copy Markdown
Member Author

Yes, but X is validated in kneighbors right after. So in order to do that we need a bit of refactoring, probably through a private _kneighbors function

@jeremiedbb

Copy link
Copy Markdown
Member Author

I think it's not the role of the lower level ArgKMin API to have to deal with non-numpy inputs. I think it's the estimators role to make sure that it calls its Cython backend with the proper datastructure.

I agree but I wanted a quick fix. I'm okay to make the refactoring I explained above as part of this PR if you think it's better

@jjerphan

jjerphan commented Jul 6, 2023

Copy link
Copy Markdown
Member

But shouldn't we just validate the data in predict to make it a c-contiguous array instead?

This might be costly to from Fortran-contiguous data to C-contiguous datasets, but we might want to consider a configuration option to specify the behavior.

FYI, this is one item of #25888.

@ogrisel

ogrisel commented Jul 6, 2023

Copy link
Copy Markdown
Member

This might be costly to from Fortran-contiguous data to C-contiguous datasets, but we might want to consider a configuration option to specify the behavior.

Indeed, this might increase memory usage by a lot for such users. But it should also make the code run significantly faster. Maybe we could extend the pairwise distance + reduction Cython classes to accept large F-aligned input, in which case the outer loop would load rowise slices of the input in parallel into C-contiguous chunks to perform the actual computation in parallel on rows of X_test.

Ok let's be pragmatic and merge this quick fix and see later what to do in a PR for 1.4.

@jjerphan

jjerphan commented Jul 6, 2023

Copy link
Copy Markdown
Member

Indeed, this might increase memory usage by a lot for such users. But it should also make the code run significantly faster.

Generally in pipeline we might have different steps which necessitates either C- or F-contiguous inputs to work (efficiently). Thus I think converting is a tradeoff whose choice which should be left to the user via the configuration for instance.

Maybe we could extend the pairwise distance + reduction Cython classes to accept large F-aligned input, in which case the outer loop would load rowise slices of the input in parallel into C-contiguous chunks to perform the actual computation in parallel on rows of X_test.

I thought of it in the past, and I do not think coming up with equivalent and efficient implementations for F-contiguous data is doable for PairwiseDistancesReductions since their memory access patterns operating on rows would not operate on continuous adresses in this case, making implementations of algorithms complex or even impossible to translate. Though, if only X_test is F-contiguous, then copying or converting it to C-contiguous vectors to use those implementations might be competitive.

@jjerphan jjerphan removed the Quick Review For PRs that are quick to review label Jul 7, 2023
@00kira00

00kira00 commented Jul 8, 2023

Copy link
Copy Markdown

hi every one so i have same prblm here 'AttributeError: 'Flags' object has no attribute 'c_contiguous''
and i dont understand the solution u given before . any one can help ?

@jjerphan

jjerphan commented Jul 9, 2023

Copy link
Copy Markdown
Member

@00kira00: the problem you describe is fixed by this PR.

@betatim
betatim merged commit f473d7e into scikit-learn:main Jul 10, 2023
@betatim

betatim commented Jul 10, 2023

Copy link
Copy Markdown
Member

Should we make an issue to discuss #26772 (comment) and what to do?

@jjerphan

Copy link
Copy Markdown
Member

Should we make an issue to discuss #26772 (comment) and what to do?

I think it makes sense.

@jjerphan jjerphan removed the Waiting for Second Reviewer First reviewer is done, need a second one! label Jul 10, 2023
punndcoder28 pushed a commit to punndcoder28/scikit-learn that referenced this pull request Jul 29, 2023
Co-authored-by: Julien Jerphanion <[email protected]>
Co-authored-by: Olivier Grisel <[email protected]>
glemaitre pushed a commit to glemaitre/scikit-learn that referenced this pull request Sep 18, 2023
Co-authored-by: Julien Jerphanion <[email protected]>
Co-authored-by: Olivier Grisel <[email protected]>
jeremiedbb added a commit that referenced this pull request Sep 20, 2023
Co-authored-by: Julien Jerphanion <[email protected]>
Co-authored-by: Olivier Grisel <[email protected]>
REDVM pushed a commit to REDVM/scikit-learn that referenced this pull request Nov 16, 2023
Co-authored-by: Julien Jerphanion <[email protected]>
Co-authored-by: Olivier Grisel <[email protected]>
@jeremiedbb
jeremiedbb deleted the fix-usable-for-dataframe branch September 3, 2026 16:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AttributeError "Flags object has no attribute 'c_contiguous'" when using KNeighborsClassifier predict

7 participants