Skip to content

Commit 2e48c71

Browse files
authored
Backport PR #55832 on branch 2.1.x (BUG: Index.getitem returning wrong result with negative step for arrow) (#56121)
1 parent eecb95f commit 2e48c71

File tree

3 files changed

+35
-3
lines changed

3 files changed

+35
-3
lines changed

doc/source/whatsnew/v2.1.4.rst

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Fixed regressions
2222
Bug fixes
2323
~~~~~~~~~
2424
- Bug in :meth:`DataFrame.apply` where passing ``raw=True`` ignored ``args`` passed to the applied function (:issue:`55753`)
25+
- Bug in :meth:`Index.__getitem__` returning wrong result for Arrow dtypes and negative stepsize (:issue:`55832`)
2526
- Fixed bug in :meth:`DataFrame.__setitem__` casting :class:`Index` with object-dtype to PyArrow backed strings when ``infer_string`` option is set (:issue:`55638`)
2627
- Fixed bug in :meth:`Index.insert` casting object-dtype to PyArrow backed strings when ``infer_string`` option is set (:issue:`55638`)
2728
-

pandas/core/arrays/arrow/array.py

+12
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,18 @@ def __getitem__(self, item: PositionalIndexer):
552552
)
553553
# We are not an array indexer, so maybe e.g. a slice or integer
554554
# indexer. We dispatch to pyarrow.
555+
if isinstance(item, slice):
556+
# Arrow bug https://github.com/apache/arrow/issues/38768
557+
if item.start == item.stop:
558+
pass
559+
elif (
560+
item.stop is not None
561+
and item.stop < -len(self)
562+
and item.step is not None
563+
and item.step < 0
564+
):
565+
item = slice(item.start, None, item.step)
566+
555567
value = self._pa_array[item]
556568
if isinstance(value, pa.ChunkedArray):
557569
return type(self)(value)

pandas/tests/indexes/object/test_indexing.py

+22-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import pytest
55

66
from pandas._libs.missing import is_matching_na
7+
import pandas.util._test_decorators as td
78

89
import pandas as pd
910
from pandas import Index
@@ -144,6 +145,13 @@ def test_get_indexer_non_unique_np_nats(self, np_nat_fixture, np_nat_fixture2):
144145

145146

146147
class TestSliceLocs:
148+
@pytest.mark.parametrize(
149+
"dtype",
150+
[
151+
"object",
152+
pytest.param("string[pyarrow_numpy]", marks=td.skip_if_no("pyarrow")),
153+
],
154+
)
147155
@pytest.mark.parametrize(
148156
"in_slice,expected",
149157
[
@@ -167,12 +175,23 @@ class TestSliceLocs:
167175
(pd.IndexSlice["m":"m":-1], ""), # type: ignore[misc]
168176
],
169177
)
170-
def test_slice_locs_negative_step(self, in_slice, expected):
171-
index = Index(list("bcdxy"))
178+
def test_slice_locs_negative_step(self, in_slice, expected, dtype):
179+
index = Index(list("bcdxy"), dtype=dtype)
172180

173181
s_start, s_stop = index.slice_locs(in_slice.start, in_slice.stop, in_slice.step)
174182
result = index[s_start : s_stop : in_slice.step]
175-
expected = Index(list(expected))
183+
expected = Index(list(expected), dtype=dtype)
184+
tm.assert_index_equal(result, expected)
185+
186+
@td.skip_if_no("pyarrow")
187+
def test_slice_locs_negative_step_oob(self):
188+
index = Index(list("bcdxy"), dtype="string[pyarrow_numpy]")
189+
190+
result = index[-10:5:1]
191+
tm.assert_index_equal(result, index)
192+
193+
result = index[4:-10:-1]
194+
expected = Index(list("yxdcb"), dtype="string[pyarrow_numpy]")
176195
tm.assert_index_equal(result, expected)
177196

178197
def test_slice_locs_dup(self):

0 commit comments

Comments
 (0)