diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index d10cb28a3f588..f0e8327e5bce4 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -432,7 +432,7 @@ Strings Interval ^^^^^^^^ - +- Bug in :meth:`IntervalIndex.take` with negative indices and ``fill_value=None`` (:issue:`37330`) - - diff --git a/pandas/core/arrays/_mixins.py b/pandas/core/arrays/_mixins.py index 726ca0ce4d776..2ac00fa803473 100644 --- a/pandas/core/arrays/_mixins.py +++ b/pandas/core/arrays/_mixins.py @@ -51,12 +51,17 @@ def take( indices: Sequence[int], allow_fill: bool = False, fill_value: Any = None, + axis: int = 0, ) -> _T: if allow_fill: fill_value = self._validate_fill_value(fill_value) new_data = take( - self._ndarray, indices, allow_fill=allow_fill, fill_value=fill_value + self._ndarray, + indices, + allow_fill=allow_fill, + fill_value=fill_value, + axis=axis, ) return self._from_backing_data(new_data) diff --git a/pandas/core/arrays/interval.py b/pandas/core/arrays/interval.py index 09488b9576212..161cf3bf3a677 100644 --- a/pandas/core/arrays/interval.py +++ b/pandas/core/arrays/interval.py @@ -544,7 +544,9 @@ def __eq__(self, other): if is_interval_dtype(other_dtype): if self.closed != other.categories.closed: return np.zeros(len(self), dtype=bool) - other = other.categories.take(other.codes) + other = other.categories.take( + other.codes, allow_fill=True, fill_value=other.categories._na_value + ) # interval-like -> need same closed and matching endpoints if is_interval_dtype(other_dtype): diff --git a/pandas/core/groupby/generic.py b/pandas/core/groupby/generic.py index 7f1c56d46fb90..08e79b45c4d27 100644 --- a/pandas/core/groupby/generic.py +++ b/pandas/core/groupby/generic.py @@ -714,7 +714,7 @@ def value_counts( # lab is a Categorical with categories an IntervalIndex lab = cut(Series(val), bins, include_lowest=True) lev = lab.cat.categories - lab = lev.take(lab.cat.codes) + lab = lev.take(lab.cat.codes, allow_fill=True, fill_value=lev._na_value) llab = lambda lab, inc: lab[inc]._multiindex.codes[-1] if is_interval_dtype(lab.dtype): diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 20a17c27fe282..48dcdfb3ecfff 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -769,7 +769,7 @@ def _assert_take_fillable( values, indices, allow_fill=allow_fill, fill_value=na_value ) else: - taken = values.take(indices) + taken = algos.take(values, indices, allow_fill=False, fill_value=na_value) return taken _index_shared_docs[ diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 7f0dc2426eba7..597b32e176170 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -909,13 +909,6 @@ def insert(self, loc, item): result = IntervalArray.from_arrays(new_left, new_right, closed=self.closed) return self._shallow_copy(result) - @Appender(_index_shared_docs["take"] % _index_doc_kwargs) - def take(self, indices, axis=0, allow_fill=True, fill_value=None, **kwargs): - result = self._data.take( - indices, axis=axis, allow_fill=allow_fill, fill_value=fill_value, **kwargs - ) - return self._shallow_copy(result) - # -------------------------------------------------------------------- # Rendering Methods # __repr__ associated methods are based on MultiIndex diff --git a/pandas/tests/indexes/categorical/test_astype.py b/pandas/tests/indexes/categorical/test_astype.py index a4a0cb1978325..44c4bcc951194 100644 --- a/pandas/tests/indexes/categorical/test_astype.py +++ b/pandas/tests/indexes/categorical/test_astype.py @@ -25,7 +25,7 @@ def test_astype(self): ) result = ci.astype("interval") - expected = ii.take([0, 1, -1]) + expected = ii.take([0, 1, -1], allow_fill=True, fill_value=np.nan) tm.assert_index_equal(result, expected) result = IntervalIndex(result.values) diff --git a/pandas/tests/indexes/common.py b/pandas/tests/indexes/common.py index b20026b20e1d7..9e248f0613893 100644 --- a/pandas/tests/indexes/common.py +++ b/pandas/tests/indexes/common.py @@ -407,6 +407,17 @@ def test_take_invalid_kwargs(self): with pytest.raises(ValueError, match=msg): idx.take(indices, mode="clip") + def test_take_minus1_without_fill(self, index): + # -1 does not get treated as NA unless allow_fill=True is passed + if len(index) == 0: + # Test is not applicable + return + + result = index.take([0, 0, -1]) + + expected = index.take([0, 0, len(index) - 1]) + tm.assert_index_equal(result, expected) + def test_repeat(self): rep = 2 i = self.create_index()