Skip to content

REV: revert deprecation of Series.__getitem__ slicing with IntegerIndex #50283

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

Merged
merged 1 commit into from
Dec 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v1.5.3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Bug fixes

Other
~~~~~
- Reverted deprecation (:issue:`45324`) of behavior of :meth:`Series.__getitem__` and :meth:`Series.__setitem__` slicing with an integer :class:`Index`; this will remain positional (:issue:`49612`)
-

.. ---------------------------------------------------------------------------
Expand Down
6 changes: 2 additions & 4 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -3786,9 +3786,7 @@ def __getitem__(self, key):
return self._getitem_multilevel(key)
# Do we have a slicer (on rows)?
if isinstance(key, slice):
indexer = self.index._convert_slice_indexer(
key, kind="getitem", is_frame=True
)
indexer = self.index._convert_slice_indexer(key, kind="getitem")
if isinstance(indexer, np.ndarray):
# reachable with DatetimeIndex
indexer = lib.maybe_indices_to_slice(
Expand Down Expand Up @@ -3967,7 +3965,7 @@ def __setitem__(self, key, value):

# see if we can slice the rows
if isinstance(key, slice):
slc = self.index._convert_slice_indexer(key, kind="getitem", is_frame=True)
slc = self.index._convert_slice_indexer(key, kind="getitem")
return self._setitem_slice(slc, value)

if isinstance(key, DataFrame) or getattr(key, "ndim", None) == 2:
Expand Down
42 changes: 1 addition & 41 deletions pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@
ABCDatetimeIndex,
ABCMultiIndex,
ABCPeriodIndex,
ABCRangeIndex,
ABCSeries,
ABCTimedeltaIndex,
)
Expand Down Expand Up @@ -3846,7 +3845,7 @@ def _validate_positional_slice(self, key: slice) -> None:
self._validate_indexer("positional", key.stop, "iloc")
self._validate_indexer("positional", key.step, "iloc")

def _convert_slice_indexer(self, key: slice, kind: str_t, is_frame: bool = False):
def _convert_slice_indexer(self, key: slice, kind: str_t):
"""
Convert a slice indexer.

Expand All @@ -3857,9 +3856,6 @@ def _convert_slice_indexer(self, key: slice, kind: str_t, is_frame: bool = False
----------
key : label of the slice bound
kind : {'loc', 'getitem'}
is_frame : bool, default False
Whether this is a slice called on DataFrame.__getitem__
as opposed to Series.__getitem__
"""
assert kind in ["loc", "getitem"], kind

Expand All @@ -3882,42 +3878,6 @@ def is_int(v):

if kind == "getitem":
# called from the getitem slicers, validate that we are in fact integers
if self.is_integer():
if is_frame:
# unambiguously positional, no deprecation
pass
elif start is None and stop is None:
# label-based vs positional is irrelevant
pass
elif isinstance(self, ABCRangeIndex) and self._range == range(
len(self)
):
# In this case there is no difference between label-based
# and positional, so nothing will change.
pass
elif (
self.dtype.kind in ["i", "u"]
and self._is_strictly_monotonic_increasing
and len(self) > 0
and self[0] == 0
and self[-1] == len(self) - 1
):
# We are range-like, e.g. created with Index(np.arange(N))
pass
elif not is_index_slice:
# we're going to raise, so don't bother warning, e.g.
# test_integer_positional_indexing
pass
else:
warnings.warn(
"The behavior of `series[i:j]` with an integer-dtype index "
"is deprecated. In a future version, this will be treated "
"as *label-based* indexing, consistent with e.g. `series[i]` "
"lookups. To retain the old behavior, use `series.iloc[i:j]`. "
"To get the future behavior, use `series.loc[i:j]`.",
FutureWarning,
stacklevel=find_stack_level(),
)
if self.is_integer() or is_index_slice:
# Note: these checks are redundant if we know is_index_slice
self._validate_indexer("slice", key.start, "getitem")
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/indexes/interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,7 @@ def _index_as_unique(self) -> bool:
"cannot handle overlapping indices; use IntervalIndex.get_indexer_non_unique"
)

def _convert_slice_indexer(self, key: slice, kind: str, is_frame: bool = False):
def _convert_slice_indexer(self, key: slice, kind: str):
if not (key.step is None or key.step == 1):
# GH#31658 if label-based, we require step == 1,
# if positional, we disallow float start/stop
Expand All @@ -808,7 +808,7 @@ def _convert_slice_indexer(self, key: slice, kind: str, is_frame: bool = False):
# i.e. this cannot be interpreted as a positional slice
raise ValueError(msg)

return super()._convert_slice_indexer(key, kind, is_frame=is_frame)
return super()._convert_slice_indexer(key, kind)

@cache_readonly
def _should_fallback_to_positional(self) -> bool:
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/indexes/numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ def _should_fallback_to_positional(self) -> bool:
return False

@doc(Index._convert_slice_indexer)
def _convert_slice_indexer(self, key: slice, kind: str, is_frame: bool = False):
def _convert_slice_indexer(self, key: slice, kind: str):
# TODO(2.0): once #45324 deprecation is enforced we should be able
# to simplify this.
if is_float_dtype(self.dtype):
Expand All @@ -231,7 +231,7 @@ def _convert_slice_indexer(self, key: slice, kind: str, is_frame: bool = False):
# translate to locations
return self.slice_indexer(key.start, key.stop, key.step)

return super()._convert_slice_indexer(key, kind=kind, is_frame=is_frame)
return super()._convert_slice_indexer(key, kind=kind)

@doc(Index._maybe_cast_slice_bound)
def _maybe_cast_slice_bound(self, label, side: str):
Expand Down
3 changes: 1 addition & 2 deletions pandas/tests/extension/base/getitem.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,7 @@ def test_get(self, data):
expected = s.iloc[[2, 3]]
self.assert_series_equal(result, expected)

with tm.assert_produces_warning(FutureWarning, match="label-based"):
result = s.get(slice(2))
result = s.get(slice(2))
expected = s.iloc[[0, 1]]
self.assert_series_equal(result, expected)

Expand Down
3 changes: 1 addition & 2 deletions pandas/tests/indexing/test_floats.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,7 @@ def test_integer_positional_indexing(self, idx):
"""
s = Series(range(2, 6), index=range(2, 6))

with tm.assert_produces_warning(FutureWarning, match="label-based"):
result = s[2:4]
result = s[2:4]
expected = s.iloc[2:4]
tm.assert_series_equal(result, expected)

Expand Down
3 changes: 1 addition & 2 deletions pandas/tests/series/indexing/test_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,7 @@ def test_get_with_ea(arr):
expected = ser.iloc[[2, 3]]
tm.assert_series_equal(result, expected)

with tm.assert_produces_warning(FutureWarning, match="label-based"):
result = ser.get(slice(2))
result = ser.get(slice(2))
expected = ser.iloc[[0, 1]]
tm.assert_series_equal(result, expected)

Expand Down
3 changes: 1 addition & 2 deletions pandas/tests/series/indexing/test_getitem.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,7 @@ def test_getitem_slice_bug(self):
def test_getitem_slice_integers(self):
ser = Series(np.random.randn(8), index=[2, 4, 6, 8, 10, 12, 14, 16])

with tm.assert_produces_warning(FutureWarning, match="label-based"):
result = ser[:4]
result = ser[:4]
expected = Series(ser.values[:4], index=[2, 4, 6, 8])
tm.assert_series_equal(result, expected)

Expand Down
12 changes: 3 additions & 9 deletions pandas/tests/series/indexing/test_setitem.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,15 +220,9 @@ def test_setitem_slice(self):
def test_setitem_slice_integers(self):
ser = Series(np.random.randn(8), index=[2, 4, 6, 8, 10, 12, 14, 16])

msg = r"In a future version, this will be treated as \*label-based\* indexing"
with tm.assert_produces_warning(FutureWarning, match=msg):
ser[:4] = 0
with tm.assert_produces_warning(
FutureWarning, match=msg, check_stacklevel=False
):
assert (ser[:4] == 0).all()
with tm.assert_produces_warning(FutureWarning, match=msg):
assert not (ser[4:] == 0).any()
ser[:4] = 0
assert (ser[:4] == 0).all()
assert not (ser[4:] == 0).any()

def test_setitem_slicestep(self):
# caught this bug when writing tests
Expand Down