-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
TST: add tests for take() on empty arrays #20582
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
4d7cc21
aba95d5
3d6fd2e
eacb3d5
ee66d61
c4faf8e
9257203
a3f88ee
b5d357f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import numpy as np | ||
|
||
import pandas as pd | ||
import pandas.util.testing as tm | ||
|
||
from .base import BaseExtensionTests | ||
|
||
|
@@ -120,3 +121,11 @@ def test_take_sequence(self, data): | |
assert result.iloc[0] == data[0] | ||
assert result.iloc[1] == data[1] | ||
assert result.iloc[2] == data[3] | ||
|
||
def test_take_empty_missing(self, data, na_value, na_cmp): | ||
empty = data[:0] | ||
result = empty.take([-1]) | ||
na_cmp(result[0], na_value) | ||
|
||
with tm.assert_raises_regex(IndexError, "cannot do a non-empty take"): | ||
empty.take([0, 1]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @TomAugspurger I didn't find any existing tests for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like the right place for testing a behavior specific to take. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -62,7 +62,7 @@ def __len__(self): | |
return len(self.values) | ||
|
||
def __repr__(self): | ||
return repr(self.values) | ||
return "DecimalArray: " + repr(self.values) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, meant to add this a while ago :) |
||
|
||
@property | ||
def nbytes(self): | ||
|
@@ -75,9 +75,19 @@ def isna(self): | |
return np.array([x.is_nan() for x in self.values]) | ||
|
||
def take(self, indexer, allow_fill=True, fill_value=None): | ||
|
||
indexer = np.asarray(indexer) | ||
mask = indexer == -1 | ||
|
||
# take on empty array | ||
if not len(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DecimalArray.take does not use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. then you need to ask yourself if this is the correct implementation. pandas implementst take_1d for exactly this reason. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
# only valid if result is an all-missing array | ||
if mask.all(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. writing take is a fair amount of effort on the extension writer. meaning additional complexity and have to handle edge cases. take is a pure indexing operation and so ideally should be implemetned as high up in the stack as possible (meaning in the base class for EA) if the EA writer wants to override then ok, but it should just work generally. All of the machinery is already there. The point of the EA is to make it easy, having to handle all of these edge cases is not easy. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Assuming you have an ndarray. Do we want to provide a default implementation that does an I'm going to make a PR documenting which methods use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It will almost never be what you want I think. It could still serve as an example implementation of course, like we now have one in the docstring. |
||
return type(self)([self._na_value] * len(indexer)) | ||
else: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no else needed. is the raise hit? |
||
raise IndexError( | ||
"cannot do a non-empty take from an empty array.") | ||
|
||
indexer = _ensure_platform_int(indexer) | ||
out = self.values.take(indexer) | ||
out[mask] = self._na_value | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the internal use cases (Categorical at the moment, later maybe more arrays), I could also push this test to
take_1d
. But, that is also used by many other things, so not sure I can just do that.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should not be here rather in take1d (i see you comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I simplified the check a bit here (as it only needed to check the empty and not-all -1 case, as when indexer is all -1
take_1d
works as expected on empty arrays).@jreback I am not sure if I should move it to
take_1d
. It depends on the guarantees we want to give (ourselves) fortake_1d
(if it should do such bounds checking). It might be that currently all codes that usestake_1d/nd
already does this bounds checking (I did not find a practical use case (apart from Categorical.take itself) where we run into this bug), and then doing another one intake_nd
would give a performance penalty.