Skip to content

DEPR: Deprecate DataFrame.last and Series.last #53710

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 8 commits into from
Jun 21, 2023
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 @@ -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`)
Expand Down
14 changes: 13 additions & 1 deletion pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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
Expand All @@ -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")

Expand Down
25 changes: 18 additions & 7 deletions pandas/tests/frame/methods/test_first_and_last.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import pandas._testing as tm

deprecated_msg = "first is deprecated"
last_deprecated_msg = "last is deprecated"


class TestFirst:
Expand Down Expand Up @@ -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)])
Expand All @@ -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)
Expand Down
20 changes: 19 additions & 1 deletion pandas/tests/generic/test_finalize.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down