Skip to content

Commit 21805a1

Browse files
authored
DEPR: resample/groupby.pad/backfill in favor of ffill/bfill (#45076)
1 parent 3570efd commit 21805a1

File tree

11 files changed

+114
-30
lines changed

11 files changed

+114
-30
lines changed

doc/source/whatsnew/v1.4.0.rst

+4
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,10 @@ Other Deprecations
609609
- Deprecated :meth:`Categorical.replace`, use :meth:`Series.replace` instead (:issue:`44929`)
610610
- Deprecated :meth:`Index.__getitem__` with a bool key; use ``index.values[key]`` to get the old behavior (:issue:`44051`)
611611
- Deprecated downcasting column-by-column in :meth:`DataFrame.where` with integer-dtypes (:issue:`44597`)
612+
- Deprecated :meth:`.Groupby.pad` in favor of :meth:`.Groupby.ffill` (:issue:`33396`)
613+
- Deprecated :meth:`.Groupby.backfill` in favor of :meth:`.Groupby.bfill` (:issue:`33396`)
614+
- Deprecated :meth:`.Resample.pad` in favor of :meth:`.Resample.ffill` (:issue:`33396`)
615+
- Deprecated :meth:`.Resample.backfill` in favor of :meth:`.Resample.bfill` (:issue:`33396`)
612616
- Deprecated ``numeric_only=None`` in :meth:`DataFrame.rank`; in a future version ``numeric_only`` must be either ``True`` or ``False`` (the default) (:issue:`45036`)
613617
- Deprecated the behavior of :meth:`Timestamp.utcfromtimestamp`, in the future it will return a timezone-aware UTC :class:`Timestamp` (:issue:`22451`)
614618
- Deprecated :meth:`NaT.freq` (:issue:`45071`)

pandas/core/groupby/base.py

+11
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@ class OutputKey:
9090
# List of transformation functions.
9191
# a transformation is a function that, for each group,
9292
# produces a result that has the same shape as the group.
93+
94+
95+
# TODO(2.0) Remove after pad/backfill deprecation enforced
96+
def maybe_normalize_deprecated_kernels(kernel):
97+
if kernel == "backfill":
98+
kernel = "bfill"
99+
elif kernel == "pad":
100+
kernel = "ffill"
101+
return kernel
102+
103+
93104
transformation_kernels = frozenset(
94105
[
95106
"backfill",

pandas/core/groupby/groupby.py

+28-10
Original file line numberDiff line numberDiff line change
@@ -2547,7 +2547,7 @@ def blk_func(values: ArrayLike) -> ArrayLike:
25472547

25482548
@final
25492549
@Substitution(name="groupby")
2550-
def pad(self, limit=None):
2550+
def ffill(self, limit=None):
25512551
"""
25522552
Forward fill the values.
25532553
@@ -2563,18 +2563,27 @@ def pad(self, limit=None):
25632563
25642564
See Also
25652565
--------
2566-
Series.pad: Returns Series with minimum number of char in object.
2567-
DataFrame.pad: Object with missing values filled or None if inplace=True.
2566+
Series.ffill: Returns Series with minimum number of char in object.
2567+
DataFrame.ffill: Object with missing values filled or None if inplace=True.
25682568
Series.fillna: Fill NaN values of a Series.
25692569
DataFrame.fillna: Fill NaN values of a DataFrame.
25702570
"""
25712571
return self._fill("ffill", limit=limit)
25722572

2573-
ffill = pad
2573+
def pad(self, limit=None):
2574+
warnings.warn(
2575+
"pad is deprecated and will be removed in a future version. "
2576+
"Use ffill instead.",
2577+
FutureWarning,
2578+
stacklevel=find_stack_level(),
2579+
)
2580+
return self.ffill(limit=limit)
2581+
2582+
pad.__doc__ = ffill.__doc__
25742583

25752584
@final
25762585
@Substitution(name="groupby")
2577-
def backfill(self, limit=None):
2586+
def bfill(self, limit=None):
25782587
"""
25792588
Backward fill the values.
25802589
@@ -2590,14 +2599,23 @@ def backfill(self, limit=None):
25902599
25912600
See Also
25922601
--------
2593-
Series.backfill : Backward fill the missing values in the dataset.
2594-
DataFrame.backfill: Backward fill the missing values in the dataset.
2602+
Series.bfill : Backward fill the missing values in the dataset.
2603+
DataFrame.bfill: Backward fill the missing values in the dataset.
25952604
Series.fillna: Fill NaN values of a Series.
25962605
DataFrame.fillna: Fill NaN values of a DataFrame.
25972606
"""
25982607
return self._fill("bfill", limit=limit)
25992608

2600-
bfill = backfill
2609+
def backfill(self, limit=None):
2610+
warnings.warn(
2611+
"backfill is deprecated and will be removed in a future version. "
2612+
"Use bfill instead.",
2613+
FutureWarning,
2614+
stacklevel=find_stack_level(),
2615+
)
2616+
return self.bfill(limit=limit)
2617+
2618+
backfill.__doc__ = bfill.__doc__
26012619

26022620
@final
26032621
@Substitution(name="groupby")
@@ -3435,7 +3453,7 @@ def shift(self, periods=1, freq=None, axis=0, fill_value=None):
34353453
@final
34363454
@Substitution(name="groupby")
34373455
@Appender(_common_see_also)
3438-
def pct_change(self, periods=1, fill_method="pad", limit=None, freq=None, axis=0):
3456+
def pct_change(self, periods=1, fill_method="ffill", limit=None, freq=None, axis=0):
34393457
"""
34403458
Calculate pct_change of each value to previous entry in group.
34413459
@@ -3457,7 +3475,7 @@ def pct_change(self, periods=1, fill_method="pad", limit=None, freq=None, axis=0
34573475
)
34583476
)
34593477
if fill_method is None: # GH30463
3460-
fill_method = "pad"
3478+
fill_method = "ffill"
34613479
limit = 0
34623480
filled = getattr(self, fill_method)(limit=limit)
34633481
fill_grp = filled.groupby(self.grouper.codes, axis=self.axis)

pandas/core/resample.py

+33-13
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
final,
1212
no_type_check,
1313
)
14+
import warnings
1415

1516
import numpy as np
1617

@@ -40,6 +41,7 @@
4041
deprecate_nonkeyword_arguments,
4142
doc,
4243
)
44+
from pandas.util._exceptions import find_stack_level
4345

4446
from pandas.core.dtypes.generic import (
4547
ABCDataFrame,
@@ -509,7 +511,7 @@ def _wrap_result(self, result):
509511

510512
return result
511513

512-
def pad(self, limit=None):
514+
def ffill(self, limit=None):
513515
"""
514516
Forward fill the values.
515517
@@ -527,9 +529,18 @@ def pad(self, limit=None):
527529
Series.fillna: Fill NA/NaN values using the specified method.
528530
DataFrame.fillna: Fill NA/NaN values using the specified method.
529531
"""
530-
return self._upsample("pad", limit=limit)
532+
return self._upsample("ffill", limit=limit)
533+
534+
def pad(self, limit=None):
535+
warnings.warn(
536+
"pad is deprecated and will be removed in a future version. "
537+
"Use ffill instead.",
538+
FutureWarning,
539+
stacklevel=find_stack_level(),
540+
)
541+
return self.ffill(limit=limit)
531542

532-
ffill = pad
543+
pad.__doc__ = ffill.__doc__
533544

534545
def nearest(self, limit=None):
535546
"""
@@ -591,7 +602,7 @@ def nearest(self, limit=None):
591602
"""
592603
return self._upsample("nearest", limit=limit)
593604

594-
def backfill(self, limit=None):
605+
def bfill(self, limit=None):
595606
"""
596607
Backward fill the new missing values in the resampled data.
597608
@@ -618,7 +629,7 @@ def backfill(self, limit=None):
618629
fillna : Fill NaN values using the specified method, which can be
619630
'backfill'.
620631
nearest : Fill NaN values with nearest neighbor starting from center.
621-
pad : Forward fill NaN values.
632+
ffill : Forward fill NaN values.
622633
Series.fillna : Fill NaN values in the Series using the
623634
specified method, which can be 'backfill'.
624635
DataFrame.fillna : Fill NaN values in the DataFrame using the
@@ -640,15 +651,15 @@ def backfill(self, limit=None):
640651
2018-01-01 02:00:00 3
641652
Freq: H, dtype: int64
642653
643-
>>> s.resample('30min').backfill()
654+
>>> s.resample('30min').bfill()
644655
2018-01-01 00:00:00 1
645656
2018-01-01 00:30:00 2
646657
2018-01-01 01:00:00 2
647658
2018-01-01 01:30:00 3
648659
2018-01-01 02:00:00 3
649660
Freq: 30T, dtype: int64
650661
651-
>>> s.resample('15min').backfill(limit=2)
662+
>>> s.resample('15min').bfill(limit=2)
652663
2018-01-01 00:00:00 1.0
653664
2018-01-01 00:15:00 NaN
654665
2018-01-01 00:30:00 2.0
@@ -671,15 +682,15 @@ def backfill(self, limit=None):
671682
2018-01-01 01:00:00 NaN 3
672683
2018-01-01 02:00:00 6.0 5
673684
674-
>>> df.resample('30min').backfill()
685+
>>> df.resample('30min').bfill()
675686
a b
676687
2018-01-01 00:00:00 2.0 1
677688
2018-01-01 00:30:00 NaN 3
678689
2018-01-01 01:00:00 NaN 3
679690
2018-01-01 01:30:00 6.0 5
680691
2018-01-01 02:00:00 6.0 5
681692
682-
>>> df.resample('15min').backfill(limit=2)
693+
>>> df.resample('15min').bfill(limit=2)
683694
a b
684695
2018-01-01 00:00:00 2.0 1.0
685696
2018-01-01 00:15:00 NaN NaN
@@ -691,9 +702,18 @@ def backfill(self, limit=None):
691702
2018-01-01 01:45:00 6.0 5.0
692703
2018-01-01 02:00:00 6.0 5.0
693704
"""
694-
return self._upsample("backfill", limit=limit)
705+
return self._upsample("bfill", limit=limit)
706+
707+
def backfill(self, limit=None):
708+
warnings.warn(
709+
"backfill is deprecated and will be removed in a future version. "
710+
"Use bfill instead.",
711+
FutureWarning,
712+
stacklevel=find_stack_level(),
713+
)
714+
return self.bfill(limit=limit)
695715

696-
bfill = backfill
716+
backfill.__doc__ = bfill.__doc__
697717

698718
def fillna(self, method, limit=None):
699719
"""
@@ -727,8 +747,8 @@ def fillna(self, method, limit=None):
727747
728748
See Also
729749
--------
730-
backfill : Backward fill NaN values in the resampled data.
731-
pad : Forward fill NaN values in the resampled data.
750+
bfill : Backward fill NaN values in the resampled data.
751+
ffill : Forward fill NaN values in the resampled data.
732752
nearest : Fill NaN values in the resampled data
733753
with nearest neighbor starting from center.
734754
interpolate : Fill NaN values using interpolation.

pandas/tests/apply/test_str.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
Series,
1313
)
1414
import pandas._testing as tm
15+
from pandas.core.groupby.base import maybe_normalize_deprecated_kernels
1516
from pandas.tests.apply.common import (
1617
frame_transform_kernels,
1718
series_transform_kernels,
@@ -245,7 +246,8 @@ def test_agg_cython_table_transform_frame(df, func, expected, axis):
245246
@pytest.mark.parametrize("op", series_transform_kernels)
246247
def test_transform_groupby_kernel_series(string_series, op):
247248
# GH 35964
248-
249+
# TODO(2.0) Remove after pad/backfill deprecation enforced
250+
op = maybe_normalize_deprecated_kernels(op)
249251
args = [0.0] if op == "fillna" else []
250252
ones = np.ones(string_series.shape[0])
251253
expected = string_series.groupby(ones).transform(op, *args)
@@ -257,6 +259,8 @@ def test_transform_groupby_kernel_series(string_series, op):
257259
def test_transform_groupby_kernel_frame(
258260
axis, float_frame, op, using_array_manager, request
259261
):
262+
# TODO(2.0) Remove after pad/backfill deprecation enforced
263+
op = maybe_normalize_deprecated_kernels(op)
260264
# GH 35964
261265
if using_array_manager and op == "pct_change" and axis in (1, "columns"):
262266
# TODO(ArrayManager) shift with axis=1

pandas/tests/groupby/test_groupby.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
)
3030
from pandas.core.base import SpecificationError
3131
import pandas.core.common as com
32+
from pandas.core.groupby.base import maybe_normalize_deprecated_kernels
3233

3334

3435
def test_repr():
@@ -2269,7 +2270,8 @@ def test_groupby_duplicate_index():
22692270
def test_dup_labels_output_shape(groupby_func, idx):
22702271
if groupby_func in {"size", "ngroup", "cumcount"}:
22712272
pytest.skip("Not applicable")
2272-
2273+
# TODO(2.0) Remove after pad/backfill deprecation enforced
2274+
groupby_func = maybe_normalize_deprecated_kernels(groupby_func)
22732275
df = DataFrame([[1, 1]], columns=idx)
22742276
grp_by = df.groupby([0])
22752277

@@ -2614,3 +2616,12 @@ def test_rolling_wrong_param_min_period():
26142616
result_error_msg = r"__init__\(\) got an unexpected keyword argument 'min_period'"
26152617
with pytest.raises(TypeError, match=result_error_msg):
26162618
test_df.groupby("name")["val"].rolling(window=2, min_period=1).sum()
2619+
2620+
2621+
def test_pad_backfill_deprecation():
2622+
# GH 33396
2623+
s = Series([1, 2, 3])
2624+
with tm.assert_produces_warning(FutureWarning, match="backfill"):
2625+
s.groupby(level=0).backfill()
2626+
with tm.assert_produces_warning(FutureWarning, match="pad"):
2627+
s.groupby(level=0).pad()

pandas/tests/groupby/test_groupby_subclass.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
Series,
99
)
1010
import pandas._testing as tm
11+
from pandas.core.groupby.base import maybe_normalize_deprecated_kernels
1112

1213

1314
@pytest.mark.parametrize(
@@ -23,7 +24,8 @@ def test_groupby_preserves_subclass(obj, groupby_func):
2324

2425
if isinstance(obj, Series) and groupby_func in {"corrwith"}:
2526
pytest.skip("Not applicable")
26-
27+
# TODO(2.0) Remove after pad/backfill deprecation enforced
28+
groupby_func = maybe_normalize_deprecated_kernels(groupby_func)
2729
grouped = obj.groupby(np.arange(0, 10))
2830

2931
# Groups should preserve subclass type

pandas/tests/groupby/transform/test_transform.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
date_range,
2121
)
2222
import pandas._testing as tm
23+
from pandas.core.groupby.base import maybe_normalize_deprecated_kernels
2324
from pandas.core.groupby.generic import (
2425
DataFrameGroupBy,
2526
SeriesGroupBy,
@@ -171,6 +172,9 @@ def test_transform_axis_1(request, transformation_func, using_array_manager):
171172
request.node.add_marker(
172173
pytest.mark.xfail(reason="ArrayManager: shift axis=1 not yet implemented")
173174
)
175+
# TODO(2.0) Remove after pad/backfill deprecation enforced
176+
transformation_func = maybe_normalize_deprecated_kernels(transformation_func)
177+
174178
warn = None
175179
if transformation_func == "tshift":
176180
warn = FutureWarning
@@ -357,7 +361,8 @@ def test_transform_transformation_func(request, transformation_func):
357361
},
358362
index=date_range("2020-01-01", "2020-01-07"),
359363
)
360-
364+
# TODO(2.0) Remove after pad/backfill deprecation enforced
365+
transformation_func = maybe_normalize_deprecated_kernels(transformation_func)
361366
if transformation_func == "cumcount":
362367
test_op = lambda x: x.transform("cumcount")
363368
mock_op = lambda x: Series(range(len(x)), x.index)

pandas/tests/resample/test_datetime_index.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ def test_resample_upsample():
438438
s = Series(np.random.rand(len(dti)), dti)
439439

440440
# to minutely, by padding
441-
result = s.resample("Min").pad()
441+
result = s.resample("Min").ffill()
442442
assert len(result) == 12961
443443
assert result[0] == s[0]
444444
assert result[-1] == s[-1]
@@ -1810,7 +1810,7 @@ def test_resample_calendar_day_with_dst(
18101810
):
18111811
# GH 35219
18121812
ts = Series(1.0, date_range(first, last, freq=freq_in, tz="Europe/Amsterdam"))
1813-
result = ts.resample(freq_out).pad()
1813+
result = ts.resample(freq_out).ffill()
18141814
expected = Series(
18151815
1.0, date_range(first, exp_last, freq=freq_out, tz="Europe/Amsterdam")
18161816
)

pandas/tests/resample/test_deprecated.py

+9
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,12 @@ def test_interpolate_posargs_deprecation():
305305

306306
expected.index._data.freq = "3s"
307307
tm.assert_series_equal(result, expected)
308+
309+
310+
def test_pad_backfill_deprecation():
311+
# GH 33396
312+
s = Series([1, 2, 3], index=date_range("20180101", periods=3, freq="h"))
313+
with tm.assert_produces_warning(FutureWarning, match="backfill"):
314+
s.resample("30min").backfill()
315+
with tm.assert_produces_warning(FutureWarning, match="pad"):
316+
s.resample("30min").pad()

pandas/tests/resample/test_resampler_grouper.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ def test_methods():
227227
expected = g.B.apply(lambda x: getattr(x.resample("2s"), f)())
228228
tm.assert_series_equal(result, expected)
229229

230-
for f in ["nearest", "backfill", "ffill", "asfreq"]:
230+
for f in ["nearest", "bfill", "ffill", "asfreq"]:
231231
result = getattr(r, f)()
232232
expected = g.apply(lambda x: getattr(x.resample("2s"), f)())
233233
tm.assert_frame_equal(result, expected)

0 commit comments

Comments
 (0)