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
Show file tree
Hide file tree
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
Expand Up @@ -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]
Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
Expand Down
2 changes: 0 additions & 2 deletions pandas/api/typing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
)
from pandas.core.resample import (
DatetimeIndexResamplerGroupby,
PeriodIndexResamplerGroupby,
Resampler,
TimedeltaIndexResamplerGroupby,
TimeGrouper,
Expand Down Expand Up @@ -41,7 +40,6 @@
"JsonReader",
"NaTType",
"NAType",
"PeriodIndexResamplerGroupby",
"Resampler",
"Rolling",
"RollingGroupby",
Expand Down
1 change: 0 additions & 1 deletion pandas/core/groupby/groupby.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
159 changes: 6 additions & 153 deletions pandas/core/resample.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -81,10 +76,6 @@
timedelta_range,
)

from pandas.tseries.frequencies import (
is_subperiod,
is_superperiod,
)
from pandas.tseries.offsets import (
Day,
Tick,
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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(
Expand All @@ -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__}'"
)

Expand Down
1 change: 0 additions & 1 deletion pandas/tests/api/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,6 @@ class TestApi(Base):
"JsonReader",
"NaTType",
"NAType",
"PeriodIndexResamplerGroupby",
"Resampler",
"Rolling",
"RollingGroupby",
Expand Down
Loading