Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 3085f9f

Browse files
DEPR: remove the Period resampling deprecation (#62480)
1 parent e4ca405 commit 3085f9f

File tree

9 files changed

+93
-191
lines changed

9 files changed

+93
-191
lines changed

‎doc/source/whatsnew/v0.21.0.rst‎

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -635,22 +635,17 @@ Previous behavior:
635635
636636
New behavior:
637637

638-
.. code-block:: ipython
638+
.. ipython:: python
639639
640-
In [1]: pi = pd.period_range('2017-01', periods=12, freq='M')
640+
pi = pd.period_range('2017-01', periods=12, freq='M')
641641
642-
In [2]: s = pd.Series(np.arange(12), index=pi)
642+
s = pd.Series(np.arange(12), index=pi)
643643
644-
In [3]: resampled = s.resample('2Q').mean()
644+
resampled = s.resample('2Q').mean()
645645
646-
In [4]: resampled
647-
Out[4]:
648-
2017Q1 2.5
649-
2017Q3 8.5
650-
Freq: 2Q-DEC, dtype: float64
646+
resampled
651647
652-
In [5]: resampled.index
653-
Out[5]: PeriodIndex(['2017Q1', '2017Q3'], dtype='period[2Q-DEC]')
648+
resampled.index
654649
655650
Upsampling and calling ``.ohlc()`` previously returned a ``Series``, basically identical to calling ``.asfreq()``. OHLC upsampling now returns a DataFrame with columns ``open``, ``high``, ``low`` and ``close`` (:issue:`13083`). This is consistent with downsampling and ``DatetimeIndex`` behavior.
656651

‎doc/source/whatsnew/v2.2.0.rst‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,7 @@ Other Deprecations
664664
- Deprecated :meth:`DatetimeArray.__init__` and :meth:`TimedeltaArray.__init__`, use :func:`array` instead (:issue:`55623`)
665665
- Deprecated :meth:`Index.format`, use ``index.astype(str)`` or ``index.map(formatter)`` instead (:issue:`55413`)
666666
- Deprecated :meth:`Series.ravel`, the underlying array is already 1D, so ravel is not necessary (:issue:`52511`)
667-
- Deprecated :meth:`Series.resample` and :meth:`DataFrame.resample` with a :class:`PeriodIndex` (and the 'convention' keyword), convert to :class:`DatetimeIndex` (with ``.to_timestamp()``) before resampling instead (:issue:`53481`)
667+
- Deprecated :meth:`Series.resample` and :meth:`DataFrame.resample` with a :class:`PeriodIndex` (and the 'convention' keyword), convert to :class:`DatetimeIndex` (with ``.to_timestamp()``) before resampling instead (:issue:`53481`). Note: this deprecation was later undone in pandas 2.3.3 (:issue:`57033`)
668668
- Deprecated :meth:`Series.view`, use :meth:`Series.astype` instead to change the dtype (:issue:`20251`)
669669
- Deprecated :meth:`offsets.Tick.is_anchored`, use ``False`` instead (:issue:`55388`)
670670
- Deprecated ``core.internals`` members ``Block``, ``ExtensionBlock``, and ``DatetimeTZBlock``, use public APIs instead (:issue:`55139`)

‎doc/source/whatsnew/v2.3.3.rst‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ Bug fixes
5757

5858
- The :meth:`DataFrame.iloc` now works correctly with ``copy_on_write`` option when assigning values after subsetting the columns of a homogeneous DataFrame (:issue:`60309`)
5959

60+
Other changes
61+
~~~~~~~~~~~~~
62+
63+
- The deprecation of using :meth:`Series.resample` and :meth:`DataFrame.resample`
64+
with a :class:`PeriodIndex` (and the 'convention' keyword) has been undone.
65+
Resampling with a :class:`PeriodIndex` is supported again, but a subset of
66+
methods that return incorrect results will raise an error in pandas 3.0 (:issue:`57033`)
67+
6068

6169
.. ---------------------------------------------------------------------------
6270
.. _whatsnew_233.contributors:

‎pandas/core/generic.py‎

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8811,7 +8811,7 @@ def resample(
88118811
rule,
88128812
closed: Literal["right", "left"] | None = None,
88138813
label: Literal["right", "left"] | None = None,
8814-
convention: Literal["start", "end", "s", "e"] |lib.NoDefault=lib.no_default,
8814+
convention: Literal["start", "end", "s", "e"] ="start",
88158815
on: Level | None = None,
88168816
level: Level | None = None,
88178817
origin: str | TimestampConvertibleTypes = "start_day",
@@ -8841,9 +8841,6 @@ def resample(
88418841
convention : {{'start', 'end', 's', 'e'}}, default 'start'
88428842
For `PeriodIndex` only, controls whether to use the start or
88438843
end of `rule`.
8844-
8845-
.. deprecated:: 2.2.0
8846-
Convert PeriodIndex to DatetimeIndex before resampling instead.
88478844
on : str, optional
88488845
For a DataFrame, column to use instead of index for resampling.
88498846
Column must be datetime-like.
@@ -8999,6 +8996,55 @@ def resample(
89998996
2000年01月01日 00:06:00 26
90008997
Freq: 3min, dtype: int64
90018998
8999+
For a Series with a PeriodIndex, the keyword `convention` can be
9000+
used to control whether to use the start or end of `rule`.
9001+
9002+
Resample a year by quarter using 'start' `convention`. Values are
9003+
assigned to the first quarter of the period.
9004+
9005+
>>> s = pd.Series(
9006+
... [1, 2], index=pd.period_range("2012年01月01日", freq="Y", periods=2)
9007+
... )
9008+
>>> s
9009+
2012 1
9010+
2013 2
9011+
Freq: Y-DEC, dtype: int64
9012+
>>> s.resample("Q", convention="start").asfreq()
9013+
2012Q1 1.0
9014+
2012Q2 NaN
9015+
2012Q3 NaN
9016+
2012Q4 NaN
9017+
2013Q1 2.0
9018+
2013Q2 NaN
9019+
2013Q3 NaN
9020+
2013Q4 NaN
9021+
Freq: Q-DEC, dtype: float64
9022+
9023+
Resample quarters by month using 'end' `convention`. Values are
9024+
assigned to the last month of the period.
9025+
9026+
>>> q = pd.Series(
9027+
... [1, 2, 3, 4], index=pd.period_range("2018年01月01日", freq="Q", periods=4)
9028+
... )
9029+
>>> q
9030+
2018Q1 1
9031+
2018Q2 2
9032+
2018Q3 3
9033+
2018Q4 4
9034+
Freq: Q-DEC, dtype: int64
9035+
>>> q.resample("M", convention="end").asfreq()
9036+
2018-03 1.0
9037+
2018-04 NaN
9038+
2018-05 NaN
9039+
2018-06 2.0
9040+
2018-07 NaN
9041+
2018-08 NaN
9042+
2018-09 3.0
9043+
2018-10 NaN
9044+
2018-11 NaN
9045+
2018-12 4.0
9046+
Freq: M, dtype: float64
9047+
90029048
For DataFrame objects, the keyword `on` can be used to specify the
90039049
column instead of the index for resampling.
90049050
@@ -9135,19 +9181,6 @@ def resample(
91359181
"""
91369182
from pandas.core.resample import get_resampler
91379183

9138-
if convention is not lib.no_default:
9139-
# TODO: Enforce in 3.0 (#55968)
9140-
warnings.warn(
9141-
f"The 'convention' keyword in {type(self).__name__}.resample is "
9142-
"deprecated and will be removed in a future version. "
9143-
"Explicitly cast PeriodIndex to DatetimeIndex before resampling "
9144-
"instead.",
9145-
FutureWarning, # pdlint: ignore[warning_class]
9146-
stacklevel=find_stack_level(),
9147-
)
9148-
else:
9149-
convention = "start"
9150-
91519184
return get_resampler(
91529185
cast("Series | DataFrame", self),
91539186
freq=rule,

‎pandas/core/resample.py‎

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1932,13 +1932,6 @@ class PeriodIndexResampler(DatetimeIndexResampler):
19321932

19331933
@property
19341934
def _resampler_for_grouping(self):
1935-
# TODO: Enforce in 3.0 (#55968)
1936-
warnings.warn(
1937-
"Resampling a groupby with a PeriodIndex is deprecated. "
1938-
"Cast to DatetimeIndex before resampling instead.",
1939-
FutureWarning, # pdlint: ignore[warning_class]
1940-
stacklevel=find_stack_level(),
1941-
)
19421935
return PeriodIndexResamplerGroupby
19431936

19441937
def _get_binner_for_time(self):
@@ -2265,15 +2258,6 @@ def _get_resampler(self, obj: NDFrame) -> Resampler:
22652258
gpr_index=ax,
22662259
)
22672260
elif isinstance(ax, PeriodIndex):
2268-
if isinstance(ax, PeriodIndex):
2269-
# TODO: Enforce in 3.0 (#53481)
2270-
# GH#53481
2271-
warnings.warn(
2272-
"Resampling with a PeriodIndex is deprecated. "
2273-
"Cast index to DatetimeIndex before resampling instead.",
2274-
FutureWarning, # pdlint: ignore[warning_class]
2275-
stacklevel=find_stack_level(),
2276-
)
22772261
return PeriodIndexResampler(
22782262
obj,
22792263
timegrouper=self,

‎pandas/plotting/_matplotlib/timeseries.py‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,9 @@ def maybe_resample(series: Series, ax: Axes, kwargs: dict[str, Any]):
8282
)
8383
freq = ax_freq
8484
elif _is_sup(freq, ax_freq): # one is weekly
85-
# Resampling with PeriodDtype is deprecated, so we convert to
86-
# DatetimeIndex, resample, then convert back.
87-
ser_ts = series.to_timestamp()
88-
ser_d = ser_ts.resample("D").last().dropna()
89-
ser_freq = ser_d.resample(ax_freq).last().dropna()
90-
series = ser_freq.to_period(ax_freq)
85+
how = "last"
86+
series = getattr(series.resample("D"), how)().dropna()
87+
series = getattr(series.resample(ax_freq), how)().dropna()
9188
freq = ax_freq
9289
elif is_subperiod(freq, ax_freq) or _is_sub(freq, ax_freq):
9390
_upsample_others(ax, freq, kwargs)

‎pandas/tests/resample/test_base.py‎

Lines changed: 15 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,8 @@ def test_asfreq_fill_value(index):
104104
def test_resample_interpolate(index):
105105
# GH#12925
106106
df = DataFrame(range(len(index)), index=index)
107-
warn = None
108-
if isinstance(df.index, PeriodIndex):
109-
warn = FutureWarning
110-
msg = "Resampling with a PeriodIndex is deprecated"
111-
with tm.assert_produces_warning(warn, match=msg):
112-
result = df.resample("1min").asfreq().interpolate()
113-
expected = df.resample("1min").interpolate()
107+
result = df.resample("1min").asfreq().interpolate()
108+
expected = df.resample("1min").interpolate()
114109
tm.assert_frame_equal(result, expected)
115110

116111

@@ -199,13 +194,7 @@ def test_resample_empty_series(freq, index, resample_method):
199194
elif freq == "ME" and isinstance(ser.index, PeriodIndex):
200195
# index is PeriodIndex, so convert to corresponding Period freq
201196
freq = "M"
202-
203-
warn = None
204-
if isinstance(ser.index, PeriodIndex):
205-
warn = FutureWarning
206-
msg = "Resampling with a PeriodIndex is deprecated"
207-
with tm.assert_produces_warning(warn, match=msg):
208-
rs = ser.resample(freq)
197+
rs = ser.resample(freq)
209198
result = getattr(rs, resample_method)()
210199

211200
if resample_method == "ohlc":
@@ -261,9 +250,7 @@ def test_resample_nat_index_series(freq, resample_method):
261250

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

264-
msg = "Resampling with a PeriodIndex is deprecated"
265-
with tm.assert_produces_warning(FutureWarning, match=msg):
266-
rs = ser.resample(freq)
253+
rs = ser.resample(freq)
267254
result = getattr(rs, resample_method)()
268255

269256
if resample_method == "ohlc":
@@ -302,13 +289,7 @@ def test_resample_count_empty_series(freq, index, resample_method):
302289
elif freq == "ME" and isinstance(ser.index, PeriodIndex):
303290
# index is PeriodIndex, so convert to corresponding Period freq
304291
freq = "M"
305-
306-
warn = None
307-
if isinstance(ser.index, PeriodIndex):
308-
warn = FutureWarning
309-
msg = "Resampling with a PeriodIndex is deprecated"
310-
with tm.assert_produces_warning(warn, match=msg):
311-
rs = ser.resample(freq)
292+
rs = ser.resample(freq)
312293

313294
result = getattr(rs, resample_method)()
314295

@@ -338,13 +319,7 @@ def test_resample_empty_dataframe(index, freq, resample_method):
338319
elif freq == "ME" and isinstance(df.index, PeriodIndex):
339320
# index is PeriodIndex, so convert to corresponding Period freq
340321
freq = "M"
341-
342-
warn = None
343-
if isinstance(df.index, PeriodIndex):
344-
warn = FutureWarning
345-
msg = "Resampling with a PeriodIndex is deprecated"
346-
with tm.assert_produces_warning(warn, match=msg):
347-
rs = df.resample(freq, group_keys=False)
322+
rs = df.resample(freq, group_keys=False)
348323
result = getattr(rs, resample_method)()
349324
if resample_method == "ohlc":
350325
# TODO: no tests with len(df.columns) > 0
@@ -386,14 +361,7 @@ def test_resample_count_empty_dataframe(freq, index):
386361
elif freq == "ME" and isinstance(empty_frame_dti.index, PeriodIndex):
387362
# index is PeriodIndex, so convert to corresponding Period freq
388363
freq = "M"
389-
390-
warn = None
391-
if isinstance(empty_frame_dti.index, PeriodIndex):
392-
warn = FutureWarning
393-
msg = "Resampling with a PeriodIndex is deprecated"
394-
with tm.assert_produces_warning(warn, match=msg):
395-
rs = empty_frame_dti.resample(freq)
396-
result = rs.count()
364+
result = empty_frame_dti.resample(freq).count()
397365

398366
index = _asfreq_compat(empty_frame_dti.index, freq)
399367

@@ -422,14 +390,7 @@ def test_resample_size_empty_dataframe(freq, index):
422390
elif freq == "ME" and isinstance(empty_frame_dti.index, PeriodIndex):
423391
# index is PeriodIndex, so convert to corresponding Period freq
424392
freq = "M"
425-
426-
msg = "Resampling with a PeriodIndex"
427-
warn = None
428-
if isinstance(empty_frame_dti.index, PeriodIndex):
429-
warn = FutureWarning
430-
with tm.assert_produces_warning(warn, match=msg):
431-
rs = empty_frame_dti.resample(freq)
432-
result = rs.size()
393+
result = empty_frame_dti.resample(freq).size()
433394

434395
index = _asfreq_compat(empty_frame_dti.index, freq)
435396

@@ -465,21 +426,12 @@ def test_resample_apply_empty_dataframe(index, freq, method):
465426
],
466427
)
467428
@pytest.mark.parametrize("dtype", [float, int, object, "datetime64[ns]"])
468-
@pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning")
469429
def test_resample_empty_dtypes(index, dtype, resample_method):
470430
# Empty series were sometimes causing a segfault (for the functions
471431
# with Cython bounds-checking disabled) or an IndexError. We just run
472432
# them to ensure they no longer do. (GH #10228)
473-
warn = None
474-
if isinstance(index, PeriodIndex):
475-
# GH#53511
476-
index = PeriodIndex([], freq="B", name=index.name)
477-
warn = FutureWarning
478-
msg = "Resampling with a PeriodIndex is deprecated"
479-
480433
empty_series_dti = Series([], index, dtype)
481-
with tm.assert_produces_warning(warn, match=msg):
482-
rs = empty_series_dti.resample("D", group_keys=False)
434+
rs = empty_series_dti.resample("D", group_keys=False)
483435
try:
484436
getattr(rs, resample_method)()
485437
except DataError:
@@ -512,18 +464,8 @@ def test_apply_to_empty_series(index, freq):
512464
elif freq == "ME" and isinstance(ser.index, PeriodIndex):
513465
# index is PeriodIndex, so convert to corresponding Period freq
514466
freq = "M"
515-
516-
msg = "Resampling with a PeriodIndex"
517-
warn = None
518-
if isinstance(ser.index, PeriodIndex):
519-
warn = FutureWarning
520-
521-
with tm.assert_produces_warning(warn, match=msg):
522-
rs = ser.resample(freq, group_keys=False)
523-
524-
result = rs.apply(lambda x: 1)
525-
with tm.assert_produces_warning(warn, match=msg):
526-
expected = ser.resample(freq).apply("sum")
467+
result = ser.resample(freq, group_keys=False).apply(lambda x: 1)
468+
expected = ser.resample(freq).apply("sum")
527469

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

@@ -541,16 +483,8 @@ def test_resampler_is_iterable(index):
541483
series = Series(range(len(index)), index=index)
542484
freq = "h"
543485
tg = Grouper(freq=freq, convention="start")
544-
msg = "Resampling with a PeriodIndex"
545-
warn = None
546-
if isinstance(series.index, PeriodIndex):
547-
warn = FutureWarning
548-
549-
with tm.assert_produces_warning(warn, match=msg):
550-
grouped = series.groupby(tg)
551-
552-
with tm.assert_produces_warning(warn, match=msg):
553-
resampled = series.resample(freq)
486+
grouped = series.groupby(tg)
487+
resampled = series.resample(freq)
554488
for (rk, rv), (gk, gv) in zip(resampled, grouped):
555489
assert rk == gk
556490
tm.assert_series_equal(rv, gv)
@@ -570,13 +504,8 @@ def test_resample_quantile(index):
570504
q = 0.75
571505
freq = "h"
572506

573-
msg = "Resampling with a PeriodIndex"
574-
warn = None
575-
if isinstance(ser.index, PeriodIndex):
576-
warn = FutureWarning
577-
with tm.assert_produces_warning(warn, match=msg):
578-
result = ser.resample(freq).quantile(q)
579-
expected = ser.resample(freq).agg(lambda x: x.quantile(q)).rename(ser.name)
507+
result = ser.resample(freq).quantile(q)
508+
expected = ser.resample(freq).agg(lambda x: x.quantile(q)).rename(ser.name)
580509
tm.assert_series_equal(result, expected)
581510

582511

‎pandas/tests/resample/test_datetime_index.py‎

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,6 @@ def test_resample_basic_grouper(unit):
169169
tm.assert_series_equal(result, expected)
170170

171171

172-
@pytest.mark.filterwarnings(
173-
"ignore:The 'convention' keyword in Series.resample:FutureWarning"
174-
)
175172
@pytest.mark.parametrize(
176173
"keyword,value",
177174
[("label", "righttt"), ("closed", "righttt"), ("convention", "starttt")],
@@ -1014,10 +1011,7 @@ def test_period_with_agg():
10141011
)
10151012

10161013
expected = s2.to_timestamp().resample("D").mean().to_period()
1017-
msg = "Resampling with a PeriodIndex is deprecated"
1018-
with tm.assert_produces_warning(FutureWarning, match=msg):
1019-
rs = s2.resample("D")
1020-
result = rs.agg(lambda x: x.mean())
1014+
result = s2.resample("D").agg(lambda x: x.mean())
10211015
tm.assert_series_equal(result, expected)
10221016

10231017

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /