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 2ca088d

Browse files
[backport 2.3.x] DEPR: remove the Period resampling deprecation (#62480) (#62485)
1 parent 92bf98f commit 2ca088d

File tree

9 files changed

+94
-187
lines changed

9 files changed

+94
-187
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
@@ -662,7 +662,7 @@ Other Deprecations
662662
- Deprecated :meth:`DatetimeArray.__init__` and :meth:`TimedeltaArray.__init__`, use :func:`array` instead (:issue:`55623`)
663663
- Deprecated :meth:`Index.format`, use ``index.astype(str)`` or ``index.map(formatter)`` instead (:issue:`55413`)
664664
- Deprecated :meth:`Series.ravel`, the underlying array is already 1D, so ravel is not necessary (:issue:`52511`)
665-
- 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`)
665+
- 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`)
666666
- Deprecated :meth:`Series.view`, use :meth:`Series.astype` instead to change the dtype (:issue:`20251`)
667667
- Deprecated :meth:`offsets.Tick.is_anchored`, use ``False`` instead (:issue:`55388`)
668668
- 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
Other Bug fixes
6270
~~~~~~~~~~~~~~~~

‎pandas/core/generic.py‎

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9415,7 +9415,7 @@ def resample(
94159415
axis: Axis | lib.NoDefault = lib.no_default,
94169416
closed: Literal["right", "left"] | None = None,
94179417
label: Literal["right", "left"] | None = None,
9418-
convention: Literal["start", "end", "s", "e"] |lib.NoDefault=lib.no_default,
9418+
convention: Literal["start", "end", "s", "e"] ="start",
94199419
kind: Literal["timestamp", "period"] | None | lib.NoDefault = lib.no_default,
94209420
on: Level | None = None,
94219421
level: Level | None = None,
@@ -9454,8 +9454,6 @@ def resample(
94549454
For `PeriodIndex` only, controls whether to use the start or
94559455
end of `rule`.
94569456
9457-
.. deprecated:: 2.2.0
9458-
Convert PeriodIndex to DatetimeIndex before resampling instead.
94599457
kind : {{'timestamp', 'period'}}, optional, default None
94609458
Pass 'timestamp' to convert the resulting index to a
94619459
`DateTimeIndex` or 'period' to convert it to a `PeriodIndex`.
@@ -9620,6 +9618,55 @@ def resample(
96209618
2000年01月01日 00:06:00 26
96219619
Freq: 3min, dtype: int64
96229620
9621+
For a Series with a PeriodIndex, the keyword `convention` can be
9622+
used to control whether to use the start or end of `rule`.
9623+
9624+
Resample a year by quarter using 'start' `convention`. Values are
9625+
assigned to the first quarter of the period.
9626+
9627+
>>> s = pd.Series(
9628+
... [1, 2], index=pd.period_range("2012年01月01日", freq="Y", periods=2)
9629+
... )
9630+
>>> s
9631+
2012 1
9632+
2013 2
9633+
Freq: Y-DEC, dtype: int64
9634+
>>> s.resample("Q", convention="start").asfreq()
9635+
2012Q1 1.0
9636+
2012Q2 NaN
9637+
2012Q3 NaN
9638+
2012Q4 NaN
9639+
2013Q1 2.0
9640+
2013Q2 NaN
9641+
2013Q3 NaN
9642+
2013Q4 NaN
9643+
Freq: Q-DEC, dtype: float64
9644+
9645+
Resample quarters by month using 'end' `convention`. Values are
9646+
assigned to the last month of the period.
9647+
9648+
>>> q = pd.Series(
9649+
... [1, 2, 3, 4], index=pd.period_range("2018年01月01日", freq="Q", periods=4)
9650+
... )
9651+
>>> q
9652+
2018Q1 1
9653+
2018Q2 2
9654+
2018Q3 3
9655+
2018Q4 4
9656+
Freq: Q-DEC, dtype: int64
9657+
>>> q.resample("M", convention="end").asfreq()
9658+
2018-03 1.0
9659+
2018-04 NaN
9660+
2018-05 NaN
9661+
2018-06 2.0
9662+
2018-07 NaN
9663+
2018-08 NaN
9664+
2018-09 3.0
9665+
2018-10 NaN
9666+
2018-11 NaN
9667+
2018-12 4.0
9668+
Freq: M, dtype: float64
9669+
96239670
For DataFrame objects, the keyword `on` can be used to specify the
96249671
column instead of the index for resampling.
96259672
@@ -9784,18 +9831,6 @@ def resample(
97849831
else:
97859832
kind = None
97869833

9787-
if convention is not lib.no_default:
9788-
warnings.warn(
9789-
f"The 'convention' keyword in {type(self).__name__}.resample is "
9790-
"deprecated and will be removed in a future version. "
9791-
"Explicitly cast PeriodIndex to DatetimeIndex before resampling "
9792-
"instead.",
9793-
FutureWarning,
9794-
stacklevel=find_stack_level(),
9795-
)
9796-
else:
9797-
convention = "start"
9798-
97999834
return get_resampler(
98009835
cast("Series | DataFrame", self),
98019836
freq=rule,

‎pandas/core/resample.py‎

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,12 +1882,6 @@ class PeriodIndexResampler(DatetimeIndexResampler):
18821882

18831883
@property
18841884
def _resampler_for_grouping(self):
1885-
warnings.warn(
1886-
"Resampling a groupby with a PeriodIndex is deprecated. "
1887-
"Cast to DatetimeIndex before resampling instead.",
1888-
FutureWarning,
1889-
stacklevel=find_stack_level(),
1890-
)
18911885
return PeriodIndexResamplerGroupby
18921886

18931887
def _get_binner_for_time(self):
@@ -2237,15 +2231,7 @@ def _get_resampler(self, obj: NDFrame, kind=None) -> Resampler:
22372231
gpr_index=ax,
22382232
)
22392233
elif isinstance(ax, PeriodIndex) or kind == "period":
2240-
if isinstance(ax, PeriodIndex):
2241-
# GH#53481
2242-
warnings.warn(
2243-
"Resampling with a PeriodIndex is deprecated. "
2244-
"Cast index to DatetimeIndex before resampling instead.",
2245-
FutureWarning,
2246-
stacklevel=find_stack_level(),
2247-
)
2248-
else:
2234+
if not isinstance(ax, PeriodIndex):
22492235
warnings.warn(
22502236
"Resampling with kind='period' is deprecated. "
22512237
"Use datetime paths instead.",

‎pandas/plotting/_matplotlib/timeseries.py‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,9 @@ def maybe_resample(series: Series, ax: Axes, kwargs: dict[str, Any]):
8686
)
8787
freq = ax_freq
8888
elif _is_sup(freq, ax_freq): # one is weekly
89-
# Resampling with PeriodDtype is deprecated, so we convert to
90-
# DatetimeIndex, resample, then convert back.
91-
ser_ts = series.to_timestamp()
92-
ser_d = ser_ts.resample("D").last().dropna()
93-
ser_freq = ser_d.resample(ax_freq).last().dropna()
94-
series = ser_freq.to_period(ax_freq)
89+
how = "last"
90+
series = getattr(series.resample("D"), how)().dropna()
91+
series = getattr(series.resample(ax_freq), how)().dropna()
9592
freq = ax_freq
9693
elif is_subperiod(freq, ax_freq) or _is_sub(freq, ax_freq):
9794
_upsample_others(ax, freq, kwargs)

‎pandas/tests/resample/test_base.py‎

Lines changed: 15 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,8 @@ def test_asfreq_fill_value(series, create_index):
8787
def test_resample_interpolate(frame):
8888
# GH#12925
8989
df = frame
90-
warn = None
91-
if isinstance(df.index, PeriodIndex):
92-
warn = FutureWarning
93-
msg = "Resampling with a PeriodIndex is deprecated"
94-
with tm.assert_produces_warning(warn, match=msg):
95-
result = df.resample("1min").asfreq().interpolate()
96-
expected = df.resample("1min").interpolate()
90+
result = df.resample("1min").asfreq().interpolate()
91+
expected = df.resample("1min").interpolate()
9792
tm.assert_frame_equal(result, expected)
9893

9994

@@ -125,13 +120,7 @@ def test_resample_empty_series(freq, empty_series_dti, resample_method):
125120
elif freq == "ME" and isinstance(ser.index, PeriodIndex):
126121
# index is PeriodIndex, so convert to corresponding Period freq
127122
freq = "M"
128-
129-
warn = None
130-
if isinstance(ser.index, PeriodIndex):
131-
warn = FutureWarning
132-
msg = "Resampling with a PeriodIndex is deprecated"
133-
with tm.assert_produces_warning(warn, match=msg):
134-
rs = ser.resample(freq)
123+
rs = ser.resample(freq)
135124
result = getattr(rs, resample_method)()
136125

137126
if resample_method == "ohlc":
@@ -189,9 +178,7 @@ def test_resample_nat_index_series(freq, series, resample_method):
189178
ser = series.copy()
190179
ser.index = PeriodIndex([NaT] * len(ser), freq=freq)
191180

192-
msg = "Resampling with a PeriodIndex is deprecated"
193-
with tm.assert_produces_warning(FutureWarning, match=msg):
194-
rs = ser.resample(freq)
181+
rs = ser.resample(freq)
195182
result = getattr(rs, resample_method)()
196183

197184
if resample_method == "ohlc":
@@ -223,13 +210,7 @@ def test_resample_count_empty_series(freq, empty_series_dti, resample_method):
223210
elif freq == "ME" and isinstance(ser.index, PeriodIndex):
224211
# index is PeriodIndex, so convert to corresponding Period freq
225212
freq = "M"
226-
227-
warn = None
228-
if isinstance(ser.index, PeriodIndex):
229-
warn = FutureWarning
230-
msg = "Resampling with a PeriodIndex is deprecated"
231-
with tm.assert_produces_warning(warn, match=msg):
232-
rs = ser.resample(freq)
213+
rs = ser.resample(freq)
233214

234215
result = getattr(rs, resample_method)()
235216

@@ -257,13 +238,7 @@ def test_resample_empty_dataframe(empty_frame_dti, freq, resample_method):
257238
elif freq == "ME" and isinstance(df.index, PeriodIndex):
258239
# index is PeriodIndex, so convert to corresponding Period freq
259240
freq = "M"
260-
261-
warn = None
262-
if isinstance(df.index, PeriodIndex):
263-
warn = FutureWarning
264-
msg = "Resampling with a PeriodIndex is deprecated"
265-
with tm.assert_produces_warning(warn, match=msg):
266-
rs = df.resample(freq, group_keys=False)
241+
rs = df.resample(freq, group_keys=False)
267242
result = getattr(rs, resample_method)()
268243
if resample_method == "ohlc":
269244
# TODO: no tests with len(df.columns) > 0
@@ -306,14 +281,7 @@ def test_resample_count_empty_dataframe(freq, empty_frame_dti):
306281
elif freq == "ME" and isinstance(empty_frame_dti.index, PeriodIndex):
307282
# index is PeriodIndex, so convert to corresponding Period freq
308283
freq = "M"
309-
310-
warn = None
311-
if isinstance(empty_frame_dti.index, PeriodIndex):
312-
warn = FutureWarning
313-
msg = "Resampling with a PeriodIndex is deprecated"
314-
with tm.assert_produces_warning(warn, match=msg):
315-
rs = empty_frame_dti.resample(freq)
316-
result = rs.count()
284+
result = empty_frame_dti.resample(freq).count()
317285

318286
index = _asfreq_compat(empty_frame_dti.index, freq)
319287

@@ -340,14 +308,7 @@ def test_resample_size_empty_dataframe(freq, empty_frame_dti):
340308
elif freq == "ME" and isinstance(empty_frame_dti.index, PeriodIndex):
341309
# index is PeriodIndex, so convert to corresponding Period freq
342310
freq = "M"
343-
344-
msg = "Resampling with a PeriodIndex"
345-
warn = None
346-
if isinstance(empty_frame_dti.index, PeriodIndex):
347-
warn = FutureWarning
348-
with tm.assert_produces_warning(warn, match=msg):
349-
rs = empty_frame_dti.resample(freq)
350-
result = rs.size()
311+
result = empty_frame_dti.resample(freq).size()
351312

352313
index = _asfreq_compat(empty_frame_dti.index, freq)
353314

@@ -365,21 +326,12 @@ def test_resample_size_empty_dataframe(freq, empty_frame_dti):
365326
],
366327
)
367328
@pytest.mark.parametrize("dtype", [float, int, object, "datetime64[ns]"])
368-
@pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning")
369329
def test_resample_empty_dtypes(index, dtype, resample_method):
370330
# Empty series were sometimes causing a segfault (for the functions
371331
# with Cython bounds-checking disabled) or an IndexError. We just run
372332
# them to ensure they no longer do. (GH #10228)
373-
warn = None
374-
if isinstance(index, PeriodIndex):
375-
# GH#53511
376-
index = PeriodIndex([], freq="B", name=index.name)
377-
warn = FutureWarning
378-
msg = "Resampling with a PeriodIndex is deprecated"
379-
380333
empty_series_dti = Series([], index, dtype)
381-
with tm.assert_produces_warning(warn, match=msg):
382-
rs = empty_series_dti.resample("d", group_keys=False)
334+
rs = empty_series_dti.resample("d", group_keys=False)
383335
try:
384336
getattr(rs, resample_method)()
385337
except DataError:
@@ -406,17 +358,8 @@ def test_apply_to_empty_series(empty_series_dti, freq):
406358
# index is PeriodIndex, so convert to corresponding Period freq
407359
freq = "M"
408360

409-
msg = "Resampling with a PeriodIndex"
410-
warn = None
411-
if isinstance(empty_series_dti.index, PeriodIndex):
412-
warn = FutureWarning
413-
414-
with tm.assert_produces_warning(warn, match=msg):
415-
rs = ser.resample(freq, group_keys=False)
416-
417-
result = rs.apply(lambda x: 1)
418-
with tm.assert_produces_warning(warn, match=msg):
419-
expected = ser.resample(freq).apply("sum")
361+
result = ser.resample(freq, group_keys=False).apply(lambda x: 1)
362+
expected = ser.resample(freq).apply("sum")
420363

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

@@ -426,16 +369,8 @@ def test_resampler_is_iterable(series):
426369
# GH 15314
427370
freq = "h"
428371
tg = Grouper(freq=freq, convention="start")
429-
msg = "Resampling with a PeriodIndex"
430-
warn = None
431-
if isinstance(series.index, PeriodIndex):
432-
warn = FutureWarning
433-
434-
with tm.assert_produces_warning(warn, match=msg):
435-
grouped = series.groupby(tg)
436-
437-
with tm.assert_produces_warning(warn, match=msg):
438-
resampled = series.resample(freq)
372+
grouped = series.groupby(tg)
373+
resampled = series.resample(freq)
439374
for (rk, rv), (gk, gv) in zip(resampled, grouped):
440375
assert rk == gk
441376
tm.assert_series_equal(rv, gv)
@@ -448,13 +383,8 @@ def test_resample_quantile(series):
448383
q = 0.75
449384
freq = "h"
450385

451-
msg = "Resampling with a PeriodIndex"
452-
warn = None
453-
if isinstance(series.index, PeriodIndex):
454-
warn = FutureWarning
455-
with tm.assert_produces_warning(warn, match=msg):
456-
result = ser.resample(freq).quantile(q)
457-
expected = ser.resample(freq).agg(lambda x: x.quantile(q)).rename(ser.name)
386+
result = ser.resample(freq).quantile(q)
387+
expected = ser.resample(freq).agg(lambda x: x.quantile(q)).rename(ser.name)
458388
tm.assert_series_equal(result, expected)
459389

460390

‎pandas/tests/resample/test_datetime_index.py‎

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

186186

187-
@pytest.mark.filterwarnings(
188-
"ignore:The 'convention' keyword in Series.resample:FutureWarning"
189-
)
190187
@pytest.mark.parametrize(
191188
"_index_start,_index_end,_index_name",
192189
[("1/1/2000 00:00:00", "1/1/2000 00:13:00", "index")],
@@ -1058,10 +1055,7 @@ def test_period_with_agg():
10581055
)
10591056

10601057
expected = s2.to_timestamp().resample("D").mean().to_period()
1061-
msg = "Resampling with a PeriodIndex is deprecated"
1062-
with tm.assert_produces_warning(FutureWarning, match=msg):
1063-
rs = s2.resample("D")
1064-
result = rs.agg(lambda x: x.mean())
1058+
result = s2.resample("D").agg(lambda x: x.mean())
10651059
tm.assert_series_equal(result, expected)
10661060

10671061

0 commit comments

Comments
(0)

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