Skip to content

DEPR: int slicing always positional #53338

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 11 commits into from
Aug 4, 2023
2 changes: 1 addition & 1 deletion doc/source/user_guide/missing_data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ at the new values.
# interpolate at new_index
new_index = ser.index.union(pd.Index([49.25, 49.5, 49.75, 50.25, 50.5, 50.75]))
interp_s = ser.reindex(new_index).interpolate(method="pchip")
interp_s[49:51]
interp_s.loc[49:51]

.. _scipy: https://scipy.org/
.. _documentation: https://docs.scipy.org/doc/scipy/reference/interpolate.html#univariate-interpolation
Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.13.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ Float64Index API change
Slicing is ALWAYS on the values of the index, for ``[],ix,loc`` and ALWAYS positional with ``iloc``

.. ipython:: python
:okwarning:

s[2:4]
s.loc[2:4]
Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v2.1.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ Other Deprecations
- Deprecated the "method" and "limit" keywords in ``ExtensionArray.fillna``, implement and use ``pad_or_backfill`` instead (:issue:`53621`)
- Deprecated the "method" and "limit" keywords on :meth:`Series.fillna`, :meth:`DataFrame.fillna`, :meth:`SeriesGroupBy.fillna`, :meth:`DataFrameGroupBy.fillna`, and :meth:`Resampler.fillna`, use ``obj.bfill()`` or ``obj.ffill()`` instead (:issue:`53394`)
- Deprecated the ``method`` and ``limit`` keywords in :meth:`DataFrame.replace` and :meth:`Series.replace` (:issue:`33302`)
- Deprecated the behavior of :meth:`Series.__getitem__`, :meth:`Series.__setitem__`, :meth:`DataFrame.__getitem__`, :meth:`DataFrame.__setitem__` with an integer slice on objects with a floating-dtype index, in a future version this will be treated as *positional* indexing (:issue:`49612`)
- Deprecated the use of non-supported datetime64 and timedelta64 resolutions with :func:`pandas.array`. Supported resolutions are: "s", "ms", "us", "ns" resolutions (:issue:`53058`)
- Deprecated values "pad", "ffill", "bfill", "backfill" for :meth:`Series.interpolate` and :meth:`DataFrame.interpolate`, use ``obj.ffill()`` or ``obj.bfill()`` instead (:issue:`53581`)
- Deprecated the behavior of :meth:`Index.argmax`, :meth:`Index.argmin`, :meth:`Series.argmax`, :meth:`Series.argmin` with either all-NAs and skipna=True or any-NAs and skipna=False returning -1; in a future version this will raise ``ValueError`` (:issue:`33941`, :issue:`33942`)
Expand Down
20 changes: 17 additions & 3 deletions pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4212,16 +4212,30 @@ def _convert_slice_indexer(self, key: slice, kind: Literal["loc", "getitem"]):
# potentially cast the bounds to integers
start, stop, step = key.start, key.stop, key.step

# figure out if this is a positional indexer
is_index_slice = is_valid_positional_slice(key)

# TODO(GH#50617): once Series.__[gs]etitem__ is removed we should be able
# to simplify this.
if lib.is_np_dtype(self.dtype, "f"):
# We always treat __getitem__ slicing as label-based
# translate to locations
if kind == "getitem" and is_index_slice and not start == stop and step != 0:
# exclude step=0 from the warning because it will raise anyway
# start/stop both None e.g. [:] or [::-1] won't change.
# exclude start==stop since it will be empty either way, or
# will be [:] or [::-1] which won't change
warnings.warn(
# GH#49612
"The behavior of obj[i:j] with a float-dtype index is "
"deprecated. In a future version, this will be treated as "
"positional instead of label-based. For label-based slicing, "
"use obj.loc[i:j] instead",
FutureWarning,
stacklevel=find_stack_level(),
)
return self.slice_indexer(start, stop, step)

# figure out if this is a positional indexer
is_index_slice = is_valid_positional_slice(key)

if kind == "getitem":
# called from the getitem slicers, validate that we are in fact integers
if is_index_slice:
Expand Down
4 changes: 3 additions & 1 deletion pandas/tests/frame/indexing/test_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,9 @@ def test_getitem_setitem_float_labels(self, using_array_manager):
tm.assert_frame_equal(result, expected)

df.loc[1:2] = 0
result = df[1:2]
msg = r"The behavior of obj\[i:j\] with a float-dtype index"
with tm.assert_produces_warning(FutureWarning, match=msg):
result = df[1:2]
assert (result == 0).all().all()

# #2727
Expand Down
9 changes: 7 additions & 2 deletions pandas/tests/indexing/test_floats.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,12 @@ def test_floating_misc(self, indexer_sl):
for fancy_idx in [[5, 0], np.array([5, 0])]:
tm.assert_series_equal(indexer_sl(s)[fancy_idx], expected)

warn = FutureWarning if indexer_sl is tm.setitem else None
msg = r"The behavior of obj\[i:j\] with a float-dtype index"

# all should return the same as we are slicing 'the same'
result1 = indexer_sl(s)[2:5]
with tm.assert_produces_warning(warn, match=msg):
result1 = indexer_sl(s)[2:5]
result2 = indexer_sl(s)[2.0:5.0]
result3 = indexer_sl(s)[2.0:5]
result4 = indexer_sl(s)[2.1:5]
Expand All @@ -498,7 +502,8 @@ def test_floating_misc(self, indexer_sl):
tm.assert_series_equal(result1, result4)

expected = Series([1, 2], index=[2.5, 5.0])
result = indexer_sl(s)[2:5]
with tm.assert_produces_warning(warn, match=msg):
result = indexer_sl(s)[2:5]

tm.assert_series_equal(result, expected)

Expand Down
14 changes: 7 additions & 7 deletions pandas/tests/series/methods/test_interpolate.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def test_interpolate_cubicspline(self):
new_index = ser.index.union(Index([1.25, 1.5, 1.75, 2.25, 2.5, 2.75])).astype(
float
)
result = ser.reindex(new_index).interpolate(method="cubicspline")[1:3]
result = ser.reindex(new_index).interpolate(method="cubicspline").loc[1:3]
tm.assert_series_equal(result, expected)

@td.skip_if_no_scipy
Expand All @@ -142,7 +142,7 @@ def test_interpolate_pchip(self):
).astype(float)
interp_s = ser.reindex(new_index).interpolate(method="pchip")
# does not blow up, GH5977
interp_s[49:51]
interp_s.loc[49:51]

@td.skip_if_no_scipy
def test_interpolate_akima(self):
Expand All @@ -157,7 +157,7 @@ def test_interpolate_akima(self):
float
)
interp_s = ser.reindex(new_index).interpolate(method="akima")
tm.assert_series_equal(interp_s[1:3], expected)
tm.assert_series_equal(interp_s.loc[1:3], expected)

# interpolate at new_index where `der` is a non-zero int
expected = Series(
Expand All @@ -168,7 +168,7 @@ def test_interpolate_akima(self):
float
)
interp_s = ser.reindex(new_index).interpolate(method="akima", der=1)
tm.assert_series_equal(interp_s[1:3], expected)
tm.assert_series_equal(interp_s.loc[1:3], expected)

@td.skip_if_no_scipy
def test_interpolate_piecewise_polynomial(self):
Expand All @@ -183,7 +183,7 @@ def test_interpolate_piecewise_polynomial(self):
float
)
interp_s = ser.reindex(new_index).interpolate(method="piecewise_polynomial")
tm.assert_series_equal(interp_s[1:3], expected)
tm.assert_series_equal(interp_s.loc[1:3], expected)

@td.skip_if_no_scipy
def test_interpolate_from_derivatives(self):
Expand All @@ -198,7 +198,7 @@ def test_interpolate_from_derivatives(self):
float
)
interp_s = ser.reindex(new_index).interpolate(method="from_derivatives")
tm.assert_series_equal(interp_s[1:3], expected)
tm.assert_series_equal(interp_s.loc[1:3], expected)

@pytest.mark.parametrize(
"kwargs",
Expand All @@ -218,7 +218,7 @@ def test_interpolate_corners(self, kwargs):

def test_interpolate_index_values(self):
s = Series(np.nan, index=np.sort(np.random.default_rng(2).random(30)))
s[::3] = np.random.default_rng(2).standard_normal(10)
s.loc[::3] = np.random.default_rng(2).standard_normal(10)

vals = s.index.values.astype(float)

Expand Down