From 459eb05701aeaf5966945a756a34db0951e8ac83 Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Fri, 26 Apr 2024 23:51:56 +0200 Subject: [PATCH 1/4] enforce deprecation of the behavior of obj[i:j] with a float-dtype index --- pandas/core/indexes/base.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 9acab2642f6be..c3d7a524e09e3 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3842,20 +3842,9 @@ def _convert_slice_indexer(self, key: slice, kind: Literal["loc", "getitem"]): # 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(), + raise TypeError( + "Using of obj[i:j] with a float-dtype index is not supported" ) - return self.slice_indexer(start, stop, step) if kind == "getitem": # called from the getitem slicers, validate that we are in fact integers From d1baf1008aa3e88f6204e2c161e7ddd892313db9 Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Sat, 27 Apr 2024 01:40:55 +0200 Subject: [PATCH 2/4] change the behavior to not special-case, fix test, add a note to v3.0.0. --- doc/source/whatsnew/v0.13.0.rst | 23 +++++++++++++++----- doc/source/whatsnew/v3.0.0.rst | 1 + pandas/core/indexes/base.py | 4 +--- pandas/tests/frame/indexing/test_indexing.py | 5 +---- pandas/tests/indexing/test_floats.py | 12 +++++----- 5 files changed, 28 insertions(+), 17 deletions(-) diff --git a/doc/source/whatsnew/v0.13.0.rst b/doc/source/whatsnew/v0.13.0.rst index a624e81d17db9..ceb16bc7a8d8d 100644 --- a/doc/source/whatsnew/v0.13.0.rst +++ b/doc/source/whatsnew/v0.13.0.rst @@ -342,12 +342,25 @@ Float64Index API change Slicing is ALWAYS on the values of the index, for ``[],ix,loc`` and ALWAYS positional with ``iloc`` - .. ipython:: python - :okwarning: + .. code-block:: ipython + + In [1]: s[2:4] + Out[23]: + 2.0 1 + 3.0 2 + dtype: int64 + + In [24]: s.loc[2:4] + Out[24]: + 2.0 1 + 3.0 2 + dtype: int64 - s[2:4] - s.loc[2:4] - s.iloc[2:4] + In[25]: s.iloc[2:4] + Out[25]: + 3.0 2 + 4.5 3 + dtype: int64 In float indexes, slicing using floats are allowed diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index c77348b365370..05d0e815f22f3 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -240,6 +240,7 @@ Removal of prior version deprecations/changes - Removed the "closed" and "unit" keywords in :meth:`TimedeltaIndex.__new__` (:issue:`52628`, :issue:`55499`) - All arguments in :meth:`Index.sort_values` are now keyword only (:issue:`56493`) - All arguments in :meth:`Series.to_dict` are now keyword only (:issue:`56493`) +- Changed 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 from label-based to a *positional* indexing (:issue:`58449`) - Changed the default value of ``observed`` in :meth:`DataFrame.groupby` and :meth:`Series.groupby` to ``True`` (:issue:`51811`) - Enforce deprecation in :func:`testing.assert_series_equal` and :func:`testing.assert_frame_equal` with object dtype and mismatched null-like values, which are now considered not-equal (:issue:`18463`) - Enforced deprecation ``all`` and ``any`` reductions with ``datetime64``, :class:`DatetimeTZDtype`, and :class:`PeriodDtype` dtypes (:issue:`58029`) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index c3d7a524e09e3..fbe2f78592dfa 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3842,9 +3842,7 @@ def _convert_slice_indexer(self, key: slice, kind: Literal["loc", "getitem"]): # 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: - raise TypeError( - "Using of obj[i:j] with a float-dtype index is not supported" - ) + self.loc[start:stop] if kind == "getitem": # called from the getitem slicers, validate that we are in fact integers diff --git a/pandas/tests/frame/indexing/test_indexing.py b/pandas/tests/frame/indexing/test_indexing.py index 69e6228d6efde..3fe52129d1f70 100644 --- a/pandas/tests/frame/indexing/test_indexing.py +++ b/pandas/tests/frame/indexing/test_indexing.py @@ -748,10 +748,7 @@ def test_getitem_setitem_float_labels(self): expected = df.iloc[0:2] tm.assert_frame_equal(result, expected) - expected = df.iloc[0: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] + result = df.loc[1:2] tm.assert_frame_equal(result, expected) # #2727 diff --git a/pandas/tests/indexing/test_floats.py b/pandas/tests/indexing/test_floats.py index 1fe431e12f2a1..4edb3fac6495b 100644 --- a/pandas/tests/indexing/test_floats.py +++ b/pandas/tests/indexing/test_floats.py @@ -491,11 +491,11 @@ 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' - with tm.assert_produces_warning(warn, match=msg): + + if indexer_sl is tm.setitem: + result1 = indexer_sl(s).loc[2:5] + else: result1 = indexer_sl(s)[2:5] result2 = indexer_sl(s)[2.0:5.0] result3 = indexer_sl(s)[2.0:5] @@ -505,7 +505,9 @@ def test_floating_misc(self, indexer_sl): tm.assert_series_equal(result1, result4) expected = Series([1, 2], index=[2.5, 5.0]) - with tm.assert_produces_warning(warn, match=msg): + if indexer_sl is tm.setitem: + result = indexer_sl(s).loc[2:5] + else: result = indexer_sl(s)[2:5] tm.assert_series_equal(result, expected) From 510ac3672b75e933455689891700ecca96d160ef Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Sat, 27 Apr 2024 02:01:09 +0200 Subject: [PATCH 3/4] fix mypy error --- pandas/core/indexes/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index fbe2f78592dfa..a9085424b0ced 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3842,7 +3842,7 @@ def _convert_slice_indexer(self, key: slice, kind: Literal["loc", "getitem"]): # 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: - self.loc[start:stop] + self.loc[start:stop] # type: ignore[attr-defined] if kind == "getitem": # called from the getitem slicers, validate that we are in fact integers From 576ff0f1561aeabfe31edf5cd7f192a215286919 Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Sat, 27 Apr 2024 08:26:29 +0200 Subject: [PATCH 4/4] fixup benchmarks/indexing.py --- asv_bench/benchmarks/indexing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asv_bench/benchmarks/indexing.py b/asv_bench/benchmarks/indexing.py index 15e691d46f693..7d34b6aa9372f 100644 --- a/asv_bench/benchmarks/indexing.py +++ b/asv_bench/benchmarks/indexing.py @@ -47,7 +47,7 @@ def time_getitem_scalar(self, index, index_structure): self.data[800000] def time_getitem_slice(self, index, index_structure): - self.data[:800000] + self.data.loc[:800000] def time_getitem_list_like(self, index, index_structure): self.data[[800000]]