diff --git a/doc/source/whatsnew/v2.1.0.rst b/doc/source/whatsnew/v2.1.0.rst index 44691e4265f5b..313f2fc728eab 100644 --- a/doc/source/whatsnew/v2.1.0.rst +++ b/doc/source/whatsnew/v2.1.0.rst @@ -288,6 +288,7 @@ Deprecations - Deprecated :func:`value_counts`, use ``pd.Series(obj).value_counts()`` instead (:issue:`47862`) - Deprecated :meth:`Series.first` and :meth:`DataFrame.first` (please create a mask and filter using ``.loc`` instead) (:issue:`45908`) - Deprecated :meth:`Series.interpolate` and :meth:`DataFrame.interpolate` for object-dtype (:issue:`53631`) +- Deprecated :meth:`Series.last` and :meth:`DataFrame.last` (please create a mask and filter using ``.loc`` instead) (:issue:`53692`) - Deprecated allowing ``downcast`` keyword other than ``None``, ``False``, "infer", or a dict with these as values in :meth:`Series.fillna`, :meth:`DataFrame.fillna` (:issue:`40988`) - Deprecated allowing arbitrary ``fill_value`` in :class:`SparseDtype`, in a future version the ``fill_value`` will need to be compatible with the ``dtype.subtype``, either a scalar that can be held by that subtype or ``NaN`` for integer or bool subtypes (:issue:`23124`) - Deprecated behavior of :func:`assert_series_equal` and :func:`assert_frame_equal` considering NA-like values (e.g. ``NaN`` vs ``None`` as equivalent) (:issue:`52081`) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index f635cc28ed4fa..5145a929247bd 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -9321,6 +9321,11 @@ def last(self, offset) -> Self: at_time : Select values at a particular time of the day. between_time : Select values between particular times of the day. + Notes + ----- + .. deprecated:: 2.1.0 + Please create a mask and filter using `.loc` instead + Examples -------- >>> i = pd.date_range('2018-04-09', periods=4, freq='2D') @@ -9334,7 +9339,7 @@ def last(self, offset) -> Self: Get the rows for the last 3 days: - >>> ts.last('3D') + >>> ts.last('3D') # doctest: +SKIP A 2018-04-13 3 2018-04-15 4 @@ -9343,6 +9348,13 @@ def last(self, offset) -> Self: 3 observed days in the dataset, and therefore data for 2018-04-11 was not returned. """ + warnings.warn( + "last is deprecated and will be removed in a future version. " + "Please create a mask and filter using `.loc` instead", + FutureWarning, + stacklevel=find_stack_level(), + ) + if not isinstance(self.index, DatetimeIndex): raise TypeError("'last' only supports a DatetimeIndex index") diff --git a/pandas/tests/frame/methods/test_first_and_last.py b/pandas/tests/frame/methods/test_first_and_last.py index 18173f7c66198..2e85edc7ed0ea 100644 --- a/pandas/tests/frame/methods/test_first_and_last.py +++ b/pandas/tests/frame/methods/test_first_and_last.py @@ -11,6 +11,7 @@ import pandas._testing as tm deprecated_msg = "first is deprecated" +last_deprecated_msg = "last is deprecated" class TestFirst: @@ -55,29 +56,38 @@ def test_first_last_raises(self, frame_or_series): obj.first("1D") msg = "'last' only supports a DatetimeIndex index" - with pytest.raises(TypeError, match=msg): # index is not a DatetimeIndex + with tm.assert_produces_warning( + FutureWarning, match=last_deprecated_msg + ), pytest.raises( + TypeError, match=msg + ): # index is not a DatetimeIndex obj.last("1D") def test_last_subset(self, frame_or_series): ts = tm.makeTimeDataFrame(freq="12h") ts = tm.get_obj(ts, frame_or_series) - result = ts.last("10d") + with tm.assert_produces_warning(FutureWarning, match=last_deprecated_msg): + result = ts.last("10d") assert len(result) == 20 ts = tm.makeTimeDataFrame(nper=30, freq="D") ts = tm.get_obj(ts, frame_or_series) - result = ts.last("10d") + with tm.assert_produces_warning(FutureWarning, match=last_deprecated_msg): + result = ts.last("10d") assert len(result) == 10 - result = ts.last("21D") + with tm.assert_produces_warning(FutureWarning, match=last_deprecated_msg): + result = ts.last("21D") expected = ts["2000-01-10":] tm.assert_equal(result, expected) - result = ts.last("21D") + with tm.assert_produces_warning(FutureWarning, match=last_deprecated_msg): + result = ts.last("21D") expected = ts[-21:] tm.assert_equal(result, expected) - result = ts[:0].last("3M") + with tm.assert_produces_warning(FutureWarning, match=last_deprecated_msg): + result = ts[:0].last("3M") tm.assert_equal(result, ts[:0]) @pytest.mark.parametrize("start, periods", [("2010-03-31", 1), ("2010-03-30", 2)]) @@ -104,7 +114,8 @@ def test_first_with_first_day_end_of_frq_n_greater_one(self, frame_or_series): def test_empty_not_input(self): # GH#51032 df = DataFrame(index=pd.DatetimeIndex([])) - result = df.last(offset=1) + with tm.assert_produces_warning(FutureWarning, match=last_deprecated_msg): + result = df.last(offset=1) with tm.assert_produces_warning(FutureWarning, match=deprecated_msg): result = df.first(offset=1) diff --git a/pandas/tests/generic/test_finalize.py b/pandas/tests/generic/test_finalize.py index 22460b1ea9dfe..f827eaf63a342 100644 --- a/pandas/tests/generic/test_finalize.py +++ b/pandas/tests/generic/test_finalize.py @@ -395,7 +395,8 @@ def ndframe_method(request): @pytest.mark.filterwarnings( - "ignore:DataFrame.fillna with 'method' is deprecated:FutureWarning" + "ignore:DataFrame.fillna with 'method' is deprecated:FutureWarning", + "ignore:last is deprecated:FutureWarning", ) def test_finalize_called(ndframe_method): cls, init_args, method = ndframe_method @@ -423,6 +424,23 @@ def test_finalize_first(data): assert result.attrs == {"a": 1} +@pytest.mark.parametrize( + "data", + [ + pd.Series(1, pd.date_range("2000", periods=4)), + pd.DataFrame({"A": [1, 1, 1, 1]}, pd.date_range("2000", periods=4)), + ], +) +def test_finalize_last(data): + # GH 53710 + deprecated_msg = "last is deprecated" + + data.attrs = {"a": 1} + with tm.assert_produces_warning(FutureWarning, match=deprecated_msg): + result = data.last("3D") + assert result.attrs == {"a": 1} + + @not_implemented_mark def test_finalize_called_eval_numexpr(): pytest.importorskip("numexpr")