Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 55 additions & 36 deletions pandas/core/groupby/grouper.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,42 +234,7 @@ def __new__(cls, *args, **kwargs):
if kwargs.get("freq") is not None:
from pandas.core.resample import TimeGrouper

# Deprecation warning of `base` and `loffset` since v1.1.0:
# we are raising the warning here to be able to set the `stacklevel`
# properly since we need to raise the `base` and `loffset` deprecation
# warning from three different cases:
# core/generic.py::NDFrame.resample
# core/groupby/groupby.py::GroupBy.resample
# core/groupby/grouper.py::Grouper
# raising these warnings from TimeGrouper directly would fail the test:
# tests/resample/test_deprecated.py::test_deprecating_on_loffset_and_base
# hacky way to set the stacklevel: if cls is TimeGrouper it means
# that the call comes from a pandas internal call of resample,
# otherwise it comes from pd.Grouper
stacklevel = 4 if cls is TimeGrouper else 2
if kwargs.get("base", None) is not None:
warnings.warn(
"'base' in .resample() and in Grouper() is deprecated.\n"
"The new arguments that you should use are 'offset' or 'origin'.\n"
'\n>>> df.resample(freq="3s", base=2)\n'
"\nbecomes:\n"
'\n>>> df.resample(freq="3s", offset="2s")\n',
FutureWarning,
stacklevel=stacklevel,
)

if kwargs.get("loffset", None) is not None:
warnings.warn(
"'loffset' in .resample() and in Grouper() is deprecated.\n"
'\n>>> df.resample(freq="3s", loffset="8H")\n'
"\nbecomes:\n"
"\n>>> from pandas.tseries.frequencies import to_offset"
'\n>>> df = df.resample(freq="3s").mean()'
'\n>>> df.index = df.index.to_timestamp() + to_offset("8H")\n',
FutureWarning,
stacklevel=stacklevel,
)

_check_deprecated_resample_kwargs(kwargs, origin=cls)
cls = TimeGrouper
return super().__new__(cls)

Expand Down Expand Up @@ -867,3 +832,57 @@ def _convert_grouper(axis: Index, grouper):
return grouper
else:
return grouper


def _check_deprecated_resample_kwargs(kwargs, origin):
"""
Check for use of deprecated parameters in ``resample`` and related functions.

Raises the appropriate warnings if these parameters are detected.
Only sets an approximate ``stacklevel`` for the warnings (see #37603, #36629).

Parameters
----------
kwargs : dict
Dictionary of keyword arguments to check for deprecated parameters.
origin : object
From where this function is being called; either Grouper or TimeGrouper. Used
to determine an approximate stacklevel.
"""
from pandas.core.resample import TimeGrouper

# Deprecation warning of `base` and `loffset` since v1.1.0:
# we are raising the warning here to be able to set the `stacklevel`
# properly since we need to raise the `base` and `loffset` deprecation
# warning from three different cases:
# core/generic.py::NDFrame.resample
# core/groupby/groupby.py::GroupBy.resample
# core/groupby/grouper.py::Grouper
# raising these warnings from TimeGrouper directly would fail the test:
# tests/resample/test_deprecated.py::test_deprecating_on_loffset_and_base
# hacky way to set the stacklevel: if cls is TimeGrouper it means
# that the call comes from a pandas internal call of resample,
# otherwise it comes from pd.Grouper
stacklevel = (4 if origin is TimeGrouper else 2) + 1

if kwargs.get("base", None) is not None:
warnings.warn(
"'base' in .resample() and in Grouper() is deprecated.\n"
"The new arguments that you should use are 'offset' or 'origin'.\n"
'\n>>> df.resample(freq="3s", base=2)\n'
"\nbecomes:\n"
'\n>>> df.resample(freq="3s", offset="2s")\n',
FutureWarning,
stacklevel=stacklevel,
)
if kwargs.get("loffset", None) is not None:
warnings.warn(
"'loffset' in .resample() and in Grouper() is deprecated.\n"
'\n>>> df.resample(freq="3s", loffset="8H")\n'
"\nbecomes:\n"
"\n>>> from pandas.tseries.frequencies import to_offset"
'\n>>> df = df.resample(freq="3s").mean()'
'\n>>> df.index = df.index.to_timestamp() + to_offset("8H")\n',
FutureWarning,
stacklevel=stacklevel,
)
38 changes: 19 additions & 19 deletions pandas/tests/resample/test_deprecated.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,20 @@ def test_deprecating_on_loffset_and_base():
idx = pd.date_range("2001-01-01", periods=4, freq="T")
df = DataFrame(data=4 * [range(2)], index=idx, columns=["a", "b"])

with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
pd.Grouper(freq="10s", base=0)
with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
pd.Grouper(freq="10s", loffset="0s")
with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
df.groupby("a").resample("3T", base=0).sum()
with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
df.groupby("a").resample("3T", loffset="0s").sum()
with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
df.resample("3T", base=0).sum()
with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
df.resample("3T", loffset="0s").sum()
msg = "'offset' and 'base' cannot be present at the same time"
with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
with pytest.raises(ValueError, match=msg):
df.groupby("a").resample("3T", base=0, offset=0).sum()

Expand All @@ -76,7 +76,7 @@ def test_resample_loffset_arg_type(frame, create_index, arg):
expected_index += timedelta(hours=2)
expected = DataFrame({"value": expected_means}, index=expected_index)

with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
result_agg = df.resample("2D", loffset="2H").agg(arg)

if isinstance(arg, list):
Expand All @@ -93,7 +93,7 @@ def test_resample_loffset(loffset):
rng = date_range("1/1/2000 00:00:00", "1/1/2000 00:13:00", freq="min")
s = Series(np.random.randn(14), index=rng)

with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
result = s.resample(
"5min", closed="right", label="right", loffset=loffset
).mean()
Expand All @@ -112,7 +112,7 @@ def test_resample_loffset(loffset):
# to weekly
result = ser.resample("w-sun").last()
business_day_offset = BDay()
with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
expected = ser.resample("w-sun", loffset=-business_day_offset).last()
assert result.index[0] - business_day_offset == expected.index[0]

Expand All @@ -122,7 +122,7 @@ def test_resample_loffset_upsample():
rng = date_range("1/1/2000 00:00:00", "1/1/2000 00:13:00", freq="min")
s = Series(np.random.randn(14), index=rng)

with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
result = s.resample(
"5min", closed="right", label="right", loffset=timedelta(minutes=1)
).ffill()
Expand All @@ -138,7 +138,7 @@ def test_resample_loffset_count():
rng = date_range(start_time, periods=100, freq="S")
ts = Series(np.random.randn(len(rng)), index=rng)

with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
result = ts.resample("10S", loffset="1s").count()

expected_index = date_range(start_time, periods=10, freq="10S") + timedelta(
Expand All @@ -150,7 +150,7 @@ def test_resample_loffset_count():

# Same issue should apply to .size() since it goes through
# same code path
with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
result = ts.resample("10S", loffset="1s").size()

tm.assert_series_equal(result, expected)
Expand All @@ -160,7 +160,7 @@ def test_resample_base():
rng = date_range("1/1/2000 00:00:00", "1/1/2000 02:00", freq="s")
ts = Series(np.random.randn(len(rng)), index=rng)

with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
resampled = ts.resample("5min", base=2).mean()
exp_rng = date_range("12/31/1999 23:57:00", "1/1/2000 01:57", freq="5min")
tm.assert_index_equal(resampled.index, exp_rng)
Expand All @@ -174,7 +174,7 @@ def test_resample_float_base():
s = Series(np.arange(3), index=dt)

base = 17 + 43.51 / 60
with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
result = s.resample("3min", base=base).size()
expected = Series(
3, index=pd.DatetimeIndex(["2018-11-26 16:17:43.51"], freq="3min")
Expand All @@ -196,7 +196,7 @@ def test_loffset_returns_datetimeindex(frame, kind, agg_arg):
expected_index += timedelta(hours=2)
expected = DataFrame({"value": expected_means}, index=expected_index)

with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
result_agg = df.resample("2D", loffset="2H", kind=kind).agg(agg_arg)
if isinstance(agg_arg, list):
expected.columns = pd.MultiIndex.from_tuples([("value", "mean")])
Expand Down Expand Up @@ -228,7 +228,7 @@ def test_resample_with_non_zero_base(start, end, start_freq, end_freq, base, off
# GH 23882
s = Series(0, index=pd.period_range(start, end, freq=start_freq))
s = s + np.arange(len(s))
with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
result = s.resample(end_freq, base=base).mean()
result = result.to_timestamp(end_freq)

Expand All @@ -239,7 +239,7 @@ def test_resample_with_non_zero_base(start, end, start_freq, end_freq, base, off

# to_timestamp casts 24H -> D
result = result.asfreq(end_freq) if end_freq == "24H" else result
with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
expected = s.to_timestamp().resample(end_freq, base=base).mean()
if end_freq == "M":
# TODO: is non-tick the relevant characteristic? (GH 33815)
Expand All @@ -252,7 +252,7 @@ def test_resample_base_with_timedeltaindex():
rng = timedelta_range(start="0s", periods=25, freq="s")
ts = Series(np.random.randn(len(rng)), index=rng)

with tm.assert_produces_warning(FutureWarning):
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
with_base = ts.resample("2s", base=5).mean()
without_base = ts.resample("2s").mean()

Expand Down