Skip to content

DEPR: resample with PeriodIndex #58021

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions asv_bench/benchmarks/timeseries.py
Original file line number Diff line number Diff line change
@@ -158,12 +158,11 @@ def time_method(self, method):


class ResampleSeries:
params = (["period", "datetime"], ["5min", "1D"], ["mean", "ohlc"])
params = (["datetime"], ["5min", "1D"], ["mean", "ohlc"])
param_names = ["index", "freq", "method"]

def setup(self, index, freq, method):
indexes = {
"period": period_range(start="1/1/2000", end="1/1/2001", freq="min"),
"datetime": date_range(start="1/1/2000", end="1/1/2001", freq="min"),
}
idx = indexes[index]
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
@@ -215,6 +215,7 @@ Removal of prior version deprecations/changes
- Enforced deprecation in :meth:`Series.value_counts` and :meth:`Index.value_counts` with object dtype performing dtype inference on the ``.index`` of the result (:issue:`56161`)
- Enforced deprecation of :meth:`.DataFrameGroupBy.get_group` and :meth:`.SeriesGroupBy.get_group` allowing the ``name`` argument to be a non-tuple when grouping by a list of length 1 (:issue:`54155`)
- Enforced deprecation of :meth:`Series.interpolate` and :meth:`DataFrame.interpolate` for object-dtype (:issue:`57820`)
- Enforced deprecation of :meth:`Series.resample` and :meth:`DataFrame.resample` with a :class:`PeriodIndex`, convert the index to :class:`DatetimeIndex` before resampling instead (:issue:`53481`)
- Enforced deprecation of :meth:`offsets.Tick.delta`, use ``pd.Timedelta(obj)`` instead (:issue:`55498`)
- Enforced deprecation of ``axis=None`` acting the same as ``axis=0`` in the DataFrame reductions ``sum``, ``prod``, ``std``, ``var``, and ``sem``, passing ``axis=None`` will now reduce over both axes; this is particularly the case when doing e.g. ``numpy.sum(df)`` (:issue:`21597`)
- Enforced deprecation of parsing system timezone strings to ``tzlocal``, which depended on system timezone, pass the 'tz' keyword instead (:issue:`50791`)
2 changes: 0 additions & 2 deletions pandas/api/typing/__init__.py
Original file line number Diff line number Diff line change
@@ -11,7 +11,6 @@
)
from pandas.core.resample import (
DatetimeIndexResamplerGroupby,
PeriodIndexResamplerGroupby,
Resampler,
TimedeltaIndexResamplerGroupby,
TimeGrouper,
@@ -41,7 +40,6 @@
"JsonReader",
"NaTType",
"NAType",
"PeriodIndexResamplerGroupby",
"Resampler",
"Rolling",
"RollingGroupby",
1 change: 0 additions & 1 deletion pandas/core/groupby/groupby.py
Original file line number Diff line number Diff line change
@@ -3512,7 +3512,6 @@ def resample(self, rule, *args, include_groups: bool = True, **kwargs) -> Resamp
Returns
-------
pandas.api.typing.DatetimeIndexResamplerGroupby,
pandas.api.typing.PeriodIndexResamplerGroupby, or
pandas.api.typing.TimedeltaIndexResamplerGroupby
Return a new groupby object, with type depending on the data
being resampled.
159 changes: 6 additions & 153 deletions pandas/core/resample.py
Original file line number Diff line number Diff line change
@@ -11,14 +11,12 @@
no_type_check,
overload,
)
import warnings

import numpy as np

from pandas._libs import lib
from pandas._libs.tslibs import (
BaseOffset,
IncompatibleFrequency,
NaT,
Period,
Timedelta,
@@ -32,10 +30,7 @@
Substitution,
doc,
)
from pandas.util._exceptions import (
find_stack_level,
rewrite_warning,
)
from pandas.util._exceptions import rewrite_warning

from pandas.core.dtypes.dtypes import (
ArrowDtype,
@@ -81,10 +76,6 @@
timedelta_range,
)

from pandas.tseries.frequencies import (
is_subperiod,
is_superperiod,
)
from pandas.tseries.offsets import (
Day,
Tick,
@@ -1704,127 +1695,6 @@ def _resampler_cls(self):
return DatetimeIndexResampler


class PeriodIndexResampler(DatetimeIndexResampler):
# error: Incompatible types in assignment (expression has type "PeriodIndex", base
# class "DatetimeIndexResampler" defined the type as "DatetimeIndex")
ax: PeriodIndex # type: ignore[assignment]

@property
def _resampler_for_grouping(self):
warnings.warn(
"Resampling a groupby with a PeriodIndex is deprecated. "
"Cast to DatetimeIndex before resampling instead.",
FutureWarning,
stacklevel=find_stack_level(),
)
return PeriodIndexResamplerGroupby

def _get_binner_for_time(self):
if self.kind == "timestamp":
return super()._get_binner_for_time()
return self._timegrouper._get_period_bins(self.ax)

def _convert_obj(self, obj: NDFrameT) -> NDFrameT:
obj = super()._convert_obj(obj)

if self._from_selection:
# see GH 14008, GH 12871
msg = (
"Resampling from level= or on= selection "
"with a PeriodIndex is not currently supported, "
"use .set_index(...) to explicitly set index"
)
raise NotImplementedError(msg)

# convert to timestamp
if self.kind == "timestamp":
obj = obj.to_timestamp(how=self.convention)

return obj

def _downsample(self, how, **kwargs):
"""
Downsample the cython defined function.

Parameters
----------
how : string / cython mapped function
**kwargs : kw args passed to how function
"""
# we may need to actually resample as if we are timestamps
if self.kind == "timestamp":
return super()._downsample(how, **kwargs)

ax = self.ax

if is_subperiod(ax.freq, self.freq):
# Downsampling
return self._groupby_and_aggregate(how, **kwargs)
elif is_superperiod(ax.freq, self.freq):
if how == "ohlc":
# GH #13083
# upsampling to subperiods is handled as an asfreq, which works
# for pure aggregating/reducing methods
# OHLC reduces along the time dimension, but creates multiple
# values for each period -> handle by _groupby_and_aggregate()
return self._groupby_and_aggregate(how)
return self.asfreq()
elif ax.freq == self.freq:
return self.asfreq()

raise IncompatibleFrequency(
f"Frequency {ax.freq} cannot be resampled to {self.freq}, "
"as they are not sub or super periods"
)

def _upsample(self, method, limit: int | None = None, fill_value=None):
"""
Parameters
----------
method : {'backfill', 'bfill', 'pad', 'ffill'}
Method for upsampling.
limit : int, default None
Maximum size gap to fill when reindexing.
fill_value : scalar, default None
Value to use for missing values.
"""
# we may need to actually resample as if we are timestamps
if self.kind == "timestamp":
return super()._upsample(method, limit=limit, fill_value=fill_value)

ax = self.ax
obj = self.obj
new_index = self.binner

# Start vs. end of period
memb = ax.asfreq(self.freq, how=self.convention)

# Get the fill indexer
if method == "asfreq":
method = None
indexer = memb.get_indexer(new_index, method=method, limit=limit)
new_obj = _take_new_index(
obj,
indexer,
new_index,
)
return self._wrap_result(new_obj)


# error: Definition of "ax" in base class "_GroupByMixin" is incompatible with
# definition in base class "PeriodIndexResampler"
class PeriodIndexResamplerGroupby( # type: ignore[misc]
_GroupByMixin, PeriodIndexResampler
):
"""
Provides a resample of a groupby implementation.
"""

@property
def _resampler_cls(self):
return PeriodIndexResampler


class TimedeltaIndexResampler(DatetimeIndexResampler):
# error: Incompatible types in assignment (expression has type "TimedeltaIndex",
# base class "DatetimeIndexResampler" defined the type as "DatetimeIndex")
@@ -2054,27 +1924,11 @@ def _get_resampler(self, obj: NDFrame, kind=None) -> Resampler:
gpr_index=ax,
)
elif isinstance(ax, PeriodIndex) or kind == "period":
if isinstance(ax, PeriodIndex):
raise TypeError(
# GH#53481
warnings.warn(
"Resampling with a PeriodIndex is deprecated. "
"Cast index to DatetimeIndex before resampling instead.",
FutureWarning,
stacklevel=find_stack_level(),
)
else:
warnings.warn(
"Resampling with kind='period' is deprecated. "
"Use datetime paths instead.",
FutureWarning,
stacklevel=find_stack_level(),
)
return PeriodIndexResampler(
obj,
timegrouper=self,
kind=kind,
group_keys=self.group_keys,
gpr_index=ax,
"Resample is no longer supported with PeriodIndex. "
"Cast index to DatetimeIndex (with obj.index.to_timestamp()) "
"first instead."
)
elif isinstance(ax, TimedeltaIndex):
return TimedeltaIndexResampler(
@@ -2085,8 +1939,7 @@ def _get_resampler(self, obj: NDFrame, kind=None) -> Resampler:
)

raise TypeError(
"Only valid with DatetimeIndex, "
"TimedeltaIndex or PeriodIndex, "
"Only valid with DatetimeIndex or TimedeltaIndex, "
f"but got an instance of '{type(ax).__name__}'"
)

1 change: 0 additions & 1 deletion pandas/tests/api/test_api.py
Original file line number Diff line number Diff line change
@@ -259,7 +259,6 @@ class TestApi(Base):
"JsonReader",
"NaTType",
"NAType",
"PeriodIndexResamplerGroupby",
"Resampler",
"Rolling",
"RollingGroupby",
177 changes: 35 additions & 142 deletions pandas/tests/resample/test_base.py
Original file line number Diff line number Diff line change
@@ -11,8 +11,6 @@
DatetimeIndex,
Index,
MultiIndex,
NaT,
PeriodIndex,
Series,
TimedeltaIndex,
)
@@ -75,27 +73,21 @@ def test_asfreq_fill_value(index):
[
timedelta_range("1 day", "10 day", freq="D"),
date_range(datetime(2005, 1, 1), datetime(2005, 1, 10), freq="D"),
period_range(datetime(2005, 1, 1), datetime(2005, 1, 10), freq="D"),
],
)
def test_resample_interpolate(index):
# GH#12925
df = DataFrame(range(len(index)), index=index)
warn = None
if isinstance(df.index, PeriodIndex):
warn = FutureWarning
msg = "Resampling with a PeriodIndex is deprecated"
with tm.assert_produces_warning(warn, match=msg):
result = df.resample("1min").asfreq().interpolate()
expected = df.resample("1min").interpolate()
result = df.resample("1min").asfreq().interpolate()
expected = df.resample("1min").interpolate()
tm.assert_frame_equal(result, expected)


def test_raises_on_non_datetimelike_index():
# this is a non datetimelike index
xp = DataFrame()
msg = (
"Only valid with DatetimeIndex, TimedeltaIndex or PeriodIndex, "
"Only valid with DatetimeIndex or TimedeltaIndex, "
"but got an instance of 'RangeIndex'"
)
with pytest.raises(TypeError, match=msg):
@@ -105,7 +97,6 @@ def test_raises_on_non_datetimelike_index():
@pytest.mark.parametrize(
"index",
[
PeriodIndex([], freq="D", name="a"),
DatetimeIndex([], name="a"),
TimedeltaIndex([], name="a"),
],
@@ -123,16 +114,8 @@ def test_resample_empty_series(freq, index, resample_method):
with pytest.raises(ValueError, match=msg):
ser.resample(freq)
return
elif freq == "ME" and isinstance(ser.index, PeriodIndex):
# index is PeriodIndex, so convert to corresponding Period freq
freq = "M"

warn = None
if isinstance(ser.index, PeriodIndex):
warn = FutureWarning
msg = "Resampling with a PeriodIndex is deprecated"
with tm.assert_produces_warning(warn, match=msg):
rs = ser.resample(freq)

rs = ser.resample(freq)
result = getattr(rs, resample_method)()

if resample_method == "ohlc":
@@ -150,40 +133,9 @@ def test_resample_empty_series(freq, index, resample_method):
assert result.index.freq == expected.index.freq


@pytest.mark.parametrize(
"freq",
[
pytest.param("ME", marks=pytest.mark.xfail(reason="Don't know why this fails")),
"D",
"h",
],
)
def test_resample_nat_index_series(freq, resample_method):
# GH39227

ser = Series(range(5), index=PeriodIndex([NaT] * 5, freq=freq))

msg = "Resampling with a PeriodIndex is deprecated"
with tm.assert_produces_warning(FutureWarning, match=msg):
rs = ser.resample(freq)
result = getattr(rs, resample_method)()

if resample_method == "ohlc":
expected = DataFrame(
[], index=ser.index[:0], columns=["open", "high", "low", "close"]
)
tm.assert_frame_equal(result, expected, check_dtype=False)
else:
expected = ser[:0].copy()
tm.assert_series_equal(result, expected, check_dtype=False)
tm.assert_index_equal(result.index, expected.index)
assert result.index.freq == expected.index.freq


@pytest.mark.parametrize(
"index",
[
PeriodIndex([], freq="D", name="a"),
DatetimeIndex([], name="a"),
TimedeltaIndex([], name="a"),
],
@@ -201,16 +153,8 @@ def test_resample_count_empty_series(freq, index, resample_method):
with pytest.raises(ValueError, match=msg):
ser.resample(freq)
return
elif freq == "ME" and isinstance(ser.index, PeriodIndex):
# index is PeriodIndex, so convert to corresponding Period freq
freq = "M"

warn = None
if isinstance(ser.index, PeriodIndex):
warn = FutureWarning
msg = "Resampling with a PeriodIndex is deprecated"
with tm.assert_produces_warning(warn, match=msg):
rs = ser.resample(freq)
rs = ser.resample(freq)

result = getattr(rs, resample_method)()

@@ -221,9 +165,7 @@ def test_resample_count_empty_series(freq, index, resample_method):
tm.assert_series_equal(result, expected)


@pytest.mark.parametrize(
"index", [DatetimeIndex([]), TimedeltaIndex([]), PeriodIndex([], freq="D")]
)
@pytest.mark.parametrize("index", [DatetimeIndex([]), TimedeltaIndex([])])
@pytest.mark.parametrize("freq", ["ME", "D", "h"])
def test_resample_empty_dataframe(index, freq, resample_method):
# GH13212
@@ -237,16 +179,8 @@ def test_resample_empty_dataframe(index, freq, resample_method):
with pytest.raises(ValueError, match=msg):
df.resample(freq, group_keys=False)
return
elif freq == "ME" and isinstance(df.index, PeriodIndex):
# index is PeriodIndex, so convert to corresponding Period freq
freq = "M"

warn = None
if isinstance(df.index, PeriodIndex):
warn = FutureWarning
msg = "Resampling with a PeriodIndex is deprecated"
with tm.assert_produces_warning(warn, match=msg):
rs = df.resample(freq, group_keys=False)

rs = df.resample(freq, group_keys=False)
result = getattr(rs, resample_method)()
if resample_method == "ohlc":
# TODO: no tests with len(df.columns) > 0
@@ -269,9 +203,7 @@ def test_resample_empty_dataframe(index, freq, resample_method):
# test size for GH13212 (currently stays as df)


@pytest.mark.parametrize(
"index", [DatetimeIndex([]), TimedeltaIndex([]), PeriodIndex([], freq="D")]
)
@pytest.mark.parametrize("index", [DatetimeIndex([]), TimedeltaIndex([])])
@pytest.mark.parametrize("freq", ["ME", "D", "h"])
def test_resample_count_empty_dataframe(freq, index):
# GH28427
@@ -285,16 +217,8 @@ def test_resample_count_empty_dataframe(freq, index):
with pytest.raises(ValueError, match=msg):
empty_frame_dti.resample(freq)
return
elif freq == "ME" and isinstance(empty_frame_dti.index, PeriodIndex):
# index is PeriodIndex, so convert to corresponding Period freq
freq = "M"

warn = None
if isinstance(empty_frame_dti.index, PeriodIndex):
warn = FutureWarning
msg = "Resampling with a PeriodIndex is deprecated"
with tm.assert_produces_warning(warn, match=msg):
rs = empty_frame_dti.resample(freq)

rs = empty_frame_dti.resample(freq)
result = rs.count()

index = _asfreq_compat(empty_frame_dti.index, freq)
@@ -304,9 +228,7 @@ def test_resample_count_empty_dataframe(freq, index):
tm.assert_frame_equal(result, expected)


@pytest.mark.parametrize(
"index", [DatetimeIndex([]), TimedeltaIndex([]), PeriodIndex([], freq="D")]
)
@pytest.mark.parametrize("index", [DatetimeIndex([]), TimedeltaIndex([])])
@pytest.mark.parametrize("freq", ["ME", "D", "h"])
def test_resample_size_empty_dataframe(freq, index):
# GH28427
@@ -321,16 +243,8 @@ def test_resample_size_empty_dataframe(freq, index):
with pytest.raises(ValueError, match=msg):
empty_frame_dti.resample(freq)
return
elif freq == "ME" and isinstance(empty_frame_dti.index, PeriodIndex):
# index is PeriodIndex, so convert to corresponding Period freq
freq = "M"

msg = "Resampling with a PeriodIndex"
warn = None
if isinstance(empty_frame_dti.index, PeriodIndex):
warn = FutureWarning
with tm.assert_produces_warning(warn, match=msg):
rs = empty_frame_dti.resample(freq)

rs = empty_frame_dti.resample(freq)
result = rs.size()

index = _asfreq_compat(empty_frame_dti.index, freq)
@@ -343,27 +257,18 @@ def test_resample_size_empty_dataframe(freq, index):
@pytest.mark.parametrize(
"index",
[
PeriodIndex([], freq="M", name="a"),
DatetimeIndex([], name="a"),
TimedeltaIndex([], name="a"),
],
)
@pytest.mark.parametrize("dtype", [float, int, object, "datetime64[ns]"])
@pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning")
def test_resample_empty_dtypes(index, dtype, resample_method):
# Empty series were sometimes causing a segfault (for the functions
# with Cython bounds-checking disabled) or an IndexError. We just run
# them to ensure they no longer do. (GH #10228)
warn = None
if isinstance(index, PeriodIndex):
# GH#53511
index = PeriodIndex([], freq="B", name=index.name)
warn = FutureWarning
msg = "Resampling with a PeriodIndex is deprecated"

empty_series_dti = Series([], index, dtype)
with tm.assert_produces_warning(warn, match=msg):
rs = empty_series_dti.resample("d", group_keys=False)
rs = empty_series_dti.resample("d", group_keys=False)
try:
getattr(rs, resample_method)()
except DataError:
@@ -375,7 +280,6 @@ def test_resample_empty_dtypes(index, dtype, resample_method):
@pytest.mark.parametrize(
"index",
[
PeriodIndex([], freq="D", name="a"),
DatetimeIndex([], name="a"),
TimedeltaIndex([], name="a"),
],
@@ -393,21 +297,11 @@ def test_apply_to_empty_series(index, freq):
with pytest.raises(ValueError, match=msg):
ser.resample(freq)
return
elif freq == "ME" and isinstance(ser.index, PeriodIndex):
# index is PeriodIndex, so convert to corresponding Period freq
freq = "M"

msg = "Resampling with a PeriodIndex"
warn = None
if isinstance(ser.index, PeriodIndex):
warn = FutureWarning

with tm.assert_produces_warning(warn, match=msg):
rs = ser.resample(freq, group_keys=False)
rs = ser.resample(freq, group_keys=False)

result = rs.apply(lambda x: 1)
with tm.assert_produces_warning(warn, match=msg):
expected = ser.resample(freq).apply("sum")
expected = ser.resample(freq).apply("sum")

tm.assert_series_equal(result, expected, check_dtype=False)

@@ -417,24 +311,16 @@ def test_apply_to_empty_series(index, freq):
[
timedelta_range("1 day", "10 day", freq="D"),
date_range(datetime(2005, 1, 1), datetime(2005, 1, 10), freq="D"),
period_range(datetime(2005, 1, 1), datetime(2005, 1, 10), freq="D"),
],
)
def test_resampler_is_iterable(index):
# GH 15314
series = Series(range(len(index)), index=index)
freq = "h"
tg = Grouper(freq=freq, convention="start")
msg = "Resampling with a PeriodIndex"
warn = None
if isinstance(series.index, PeriodIndex):
warn = FutureWarning

with tm.assert_produces_warning(warn, match=msg):
grouped = series.groupby(tg)

with tm.assert_produces_warning(warn, match=msg):
resampled = series.resample(freq)
grouped = series.groupby(tg)
resampled = series.resample(freq)
for (rk, rv), (gk, gv) in zip(resampled, grouped):
assert rk == gk
tm.assert_series_equal(rv, gv)
@@ -445,7 +331,6 @@ def test_resampler_is_iterable(index):
[
timedelta_range("1 day", "10 day", freq="D"),
date_range(datetime(2005, 1, 1), datetime(2005, 1, 10), freq="D"),
period_range(datetime(2005, 1, 1), datetime(2005, 1, 10), freq="D"),
],
)
def test_resample_quantile(index):
@@ -454,13 +339,8 @@ def test_resample_quantile(index):
q = 0.75
freq = "h"

msg = "Resampling with a PeriodIndex"
warn = None
if isinstance(ser.index, PeriodIndex):
warn = FutureWarning
with tm.assert_produces_warning(warn, match=msg):
result = ser.resample(freq).quantile(q)
expected = ser.resample(freq).agg(lambda x: x.quantile(q)).rename(ser.name)
result = ser.resample(freq).quantile(q)
expected = ser.resample(freq).agg(lambda x: x.quantile(q)).rename(ser.name)
tm.assert_series_equal(result, expected)


@@ -488,3 +368,16 @@ def test_first_last_skipna(any_real_nullable_dtype, skipna, how):
expected = getattr(gb, how)(skipna=skipna)
expected.index.freq = "ME"
tm.assert_frame_equal(result, expected)


def test_resample_with_period_disallowed():
# GH#53481
ser = Series(
np.random.default_rng(2).integers(0, 5, 50),
index=period_range("2012-01-01", freq="h", periods=50),
dtype="float64",
)

msg = "Resample is no longer supported with PeriodIndex"
with pytest.raises(TypeError, match=msg):
ser.resample("D")
16 changes: 0 additions & 16 deletions pandas/tests/resample/test_datetime_index.py
Original file line number Diff line number Diff line change
@@ -992,22 +992,6 @@ def test_resample_to_period_monthly_buglet(unit):
tm.assert_index_equal(result.index, exp_index)


def test_period_with_agg():
# aggregate a period resampler with a lambda
s2 = Series(
np.random.default_rng(2).integers(0, 5, 50),
index=period_range("2012-01-01", freq="h", periods=50),
dtype="float64",
)

expected = s2.to_timestamp().resample("D").mean().to_period()
msg = "Resampling with a PeriodIndex is deprecated"
with tm.assert_produces_warning(FutureWarning, match=msg):
rs = s2.resample("D")
result = rs.agg(lambda x: x.mean())
tm.assert_series_equal(result, expected)


def test_resample_segfault(unit):
# GH 8573
# segfaulting in older versions
1,091 changes: 0 additions & 1,091 deletions pandas/tests/resample/test_period_index.py

This file was deleted.

2 changes: 1 addition & 1 deletion pandas/tests/resample/test_resample_api.py
Original file line number Diff line number Diff line change
@@ -674,7 +674,7 @@ def test_selection_api_validation():

# non DatetimeIndex
msg = (
"Only valid with DatetimeIndex, TimedeltaIndex or PeriodIndex, "
"Only valid with DatetimeIndex or TimedeltaIndex, "
"but got an instance of 'Index'"
)
with pytest.raises(TypeError, match=msg):
4 changes: 2 additions & 2 deletions pandas/tests/resample/test_time_grouper.py
Original file line number Diff line number Diff line change
@@ -96,8 +96,8 @@ def test_fails_on_no_datetime_index(index):
df = DataFrame({"a": range(len(index))}, index=index)

msg = (
"Only valid with DatetimeIndex, TimedeltaIndex "
f"or PeriodIndex, but got an instance of '{name}'"
"Only valid with DatetimeIndex or TimedeltaIndex, "
f"but got an instance of '{name}'"
)
with pytest.raises(TypeError, match=msg):
df.groupby(Grouper(freq="D"))