Skip to content

Commit 1ccd611

Browse files
jbrockmendelim-vinicius
authored and
im-vinicius
committed
DEPR: fillna method kwd (pandas-dev#53496)
* DEPR: fillna method kwd * deprecate Resampler.fillna entirely * suppress doctest warnings * typo fixup * doctest * mypy fixup * lint fixup
1 parent ac08921 commit 1ccd611

29 files changed

+337
-163
lines changed

ci/code_checks.sh

+1
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
279279
pandas.core.groupby.DataFrameGroupBy.sum \
280280
pandas.core.groupby.DataFrameGroupBy.var \
281281
pandas.core.groupby.SeriesGroupBy.diff \
282+
pandas.core.groupby.SeriesGroupBy.fillna \
282283
pandas.core.groupby.SeriesGroupBy.ffill \
283284
pandas.core.groupby.SeriesGroupBy.max \
284285
pandas.core.groupby.SeriesGroupBy.median \

doc/source/getting_started/comparison/includes/missing.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Forward fill from previous rows
1919

2020
.. ipython:: python
2121
22-
outer_join.fillna(method="ffill")
22+
outer_join.ffill()
2323
2424
Replace missing values with a specified value
2525
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

doc/source/user_guide/basics.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -1377,12 +1377,12 @@ These methods require that the indexes are **ordered** increasing or
13771377
decreasing.
13781378

13791379
Note that the same result could have been achieved using
1380-
:ref:`fillna <missing_data.fillna>` (except for ``method='nearest'``) or
1380+
:ref:`ffill <missing_data.fillna>` (except for ``method='nearest'``) or
13811381
:ref:`interpolate <missing_data.interpolate>`:
13821382

13831383
.. ipython:: python
13841384
1385-
ts2.reindex(ts.index).fillna(method="ffill")
1385+
ts2.reindex(ts.index).ffill()
13861386
13871387
:meth:`~Series.reindex` will raise a ValueError if the index is not monotonically
13881388
increasing or decreasing. :meth:`~Series.fillna` and :meth:`~Series.interpolate`

doc/source/user_guide/missing_data.rst

+3-3
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ objects.
145145
:suppress:
146146
147147
df = df2.loc[:, ["one", "two", "three"]]
148-
a = df2.loc[df2.index[:5], ["one", "two"]].fillna(method="pad")
148+
a = df2.loc[df2.index[:5], ["one", "two"]].ffill()
149149
b = df2.loc[df2.index[:5], ["one", "two", "three"]]
150150
151151
.. ipython:: python
@@ -237,7 +237,7 @@ can propagate non-NA values forward or backward:
237237
.. ipython:: python
238238
239239
df
240-
df.fillna(method="pad")
240+
df.ffill()
241241
242242
.. _missing_data.fillna.limit:
243243

@@ -254,7 +254,7 @@ we can use the ``limit`` keyword:
254254
.. ipython:: python
255255
256256
df
257-
df.fillna(method="pad", limit=1)
257+
df.ffill(limit=1)
258258
259259
To remind you, these are the available filling methods:
260260

doc/source/whatsnew/v0.10.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ labeled the aggregated group with the end of the interval: the next day).
226226
valid code. You must either specify a fill value or an interpolation method:
227227

228228
.. ipython:: python
229+
:okwarning:
229230
230231
s = pd.Series([np.nan, 1.0, 2.0, np.nan, 4])
231232
s

doc/source/whatsnew/v2.1.0.rst

+2
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ Deprecations
279279
- Deprecated constructing :class:`SparseArray` from scalar data, pass a sequence instead (:issue:`53039`)
280280
- Deprecated option "mode.use_inf_as_na", convert inf entries to ``NaN`` before instead (:issue:`51684`)
281281
- Deprecated positional indexing on :class:`Series` with :meth:`Series.__getitem__` and :meth:`Series.__setitem__`, in a future version ``ser[item]`` will *always* interpret ``item`` as a label, not a position (:issue:`50617`)
282+
- Deprecated the "method" and "limit" keywords on :meth:`Series.fillna`, :meth:`DataFrame.fillna`, :meth:`SeriesGroupBy.fillna`, :meth:`DataFrameGroupBy.fillna`, and :meth:`Resampler.fillna`, use ``obj.bfill()`` or ``obj.ffill()`` instead (:issue:`53394`)
283+
-
282284

283285
.. ---------------------------------------------------------------------------
284286
.. _whatsnew_210.performance:

pandas/conftest.py

+12
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,18 @@ def pytest_collection_modifyitems(items, config) -> None:
150150
"first is deprecated and will be removed in a future version. "
151151
"Please create a mask and filter using `.loc` instead",
152152
),
153+
(
154+
"Resampler.fillna",
155+
"DatetimeIndexResampler.fillna is deprecated",
156+
),
157+
(
158+
"DataFrameGroupBy.fillna",
159+
"DataFrameGroupBy.fillna with 'method' is deprecated",
160+
),
161+
(
162+
"DataFrameGroupBy.fillna",
163+
"DataFrame.fillna with 'method' is deprecated",
164+
),
153165
]
154166

155167
for item in items:

pandas/core/generic.py

+109-45
Original file line numberDiff line numberDiff line change
@@ -6803,6 +6803,61 @@ def convert_dtypes(
68036803
# ----------------------------------------------------------------------
68046804
# Filling NA's
68056805

6806+
def _deprecate_downcast(self, downcast) -> None:
6807+
if isinstance(downcast, dict):
6808+
# GH#40988
6809+
for dc in downcast.values():
6810+
if dc is not None and dc is not False and dc != "infer":
6811+
warnings.warn(
6812+
"downcast entries other than None, False, and 'infer' "
6813+
"are deprecated and will raise in a future version",
6814+
FutureWarning,
6815+
stacklevel=find_stack_level(),
6816+
)
6817+
elif downcast is not None and downcast is not False and downcast != "infer":
6818+
# GH#40988
6819+
warnings.warn(
6820+
"downcast other than None, False, and 'infer' are deprecated "
6821+
"and will raise in a future version",
6822+
FutureWarning,
6823+
stacklevel=find_stack_level(),
6824+
)
6825+
6826+
@final
6827+
def _fillna_with_method(
6828+
self,
6829+
method: Literal["ffill", "bfill", "pad", "backfill"],
6830+
*,
6831+
axis: None | Axis = None,
6832+
inplace: bool_t = False,
6833+
limit: None | int = None,
6834+
downcast: dict | None = None,
6835+
):
6836+
if axis is None:
6837+
axis = 0
6838+
axis = self._get_axis_number(axis)
6839+
method = clean_fill_method(method)
6840+
6841+
if not self._mgr.is_single_block and axis == 1:
6842+
if inplace:
6843+
raise NotImplementedError()
6844+
result = self.T._fillna_with_method(method=method, limit=limit).T
6845+
6846+
return result
6847+
6848+
new_mgr = self._mgr.interpolate(
6849+
method=method,
6850+
axis=axis,
6851+
limit=limit,
6852+
inplace=inplace,
6853+
downcast=downcast,
6854+
)
6855+
result = self._constructor(new_mgr)
6856+
if inplace:
6857+
return self._update_inplace(result)
6858+
else:
6859+
return result.__finalize__(self, method="fillna")
6860+
68066861
@overload
68076862
def fillna(
68086863
self,
@@ -6874,6 +6929,9 @@ def fillna(
68746929
* ffill: propagate last valid observation forward to next valid.
68756930
* backfill / bfill: use next valid observation to fill gap.
68766931
6932+
.. deprecated:: 2.1.0
6933+
Use ffill or bfill instead.
6934+
68776935
axis : {axes_single_arg}
68786936
Axis along which to fill missing values. For `Series`
68796937
this parameter is unused and defaults to 0.
@@ -6927,15 +6985,6 @@ def fillna(
69276985
2 0.0 0.0 0.0 0.0
69286986
3 0.0 3.0 0.0 4.0
69296987
6930-
We can also propagate non-null values forward or backward.
6931-
6932-
>>> df.fillna(method="ffill")
6933-
A B C D
6934-
0 NaN 2.0 NaN 0.0
6935-
1 3.0 4.0 NaN 1.0
6936-
2 3.0 4.0 NaN 1.0
6937-
3 3.0 3.0 NaN 4.0
6938-
69396988
Replace all NaN elements in column 'A', 'B', 'C', and 'D', with 0, 1,
69406989
2, and 3 respectively.
69416990
@@ -6971,42 +7020,29 @@ def fillna(
69717020
"""
69727021
inplace = validate_bool_kwarg(inplace, "inplace")
69737022
value, method = validate_fillna_kwargs(value, method)
6974-
6975-
if isinstance(downcast, dict):
6976-
# GH#40988
6977-
for dc in downcast.values():
6978-
if dc is not None and dc is not False and dc != "infer":
6979-
warnings.warn(
6980-
"downcast entries other than None, False, and 'infer' "
6981-
"are deprecated and will raise in a future version",
6982-
FutureWarning,
6983-
stacklevel=find_stack_level(),
6984-
)
6985-
elif downcast is not None and downcast is not False and downcast != "infer":
6986-
# GH#40988
7023+
if method is not None:
69877024
warnings.warn(
6988-
"downcast other than None, False, and 'infer' are deprecated "
6989-
"and will raise in a future version",
7025+
f"{type(self).__name__}.fillna with 'method' is deprecated and "
7026+
"will raise in a future version. Use obj.ffill() or obj.bfill() "
7027+
"instead.",
69907028
FutureWarning,
69917029
stacklevel=find_stack_level(),
69927030
)
69937031

7032+
self._deprecate_downcast(downcast)
7033+
69947034
# set the default here, so functions examining the signaure
69957035
# can detect if something was set (e.g. in groupby) (GH9221)
69967036
if axis is None:
69977037
axis = 0
69987038
axis = self._get_axis_number(axis)
69997039

70007040
if value is None:
7001-
if not self._mgr.is_single_block and axis == 1:
7002-
if inplace:
7003-
raise NotImplementedError()
7004-
result = self.T.fillna(method=method, limit=limit).T
7005-
7006-
return result
7007-
7008-
new_data = self._mgr.interpolate(
7009-
method=method,
7041+
return self._fillna_with_method(
7042+
# error: Argument 1 to "_fillna_with_method" of "NDFrame" has
7043+
# incompatible type "Optional[Literal['backfill', 'bfill', 'ffill',
7044+
# 'pad']]"; expected "Literal['ffill', 'bfill', 'pad', 'backfill']"
7045+
method, # type: ignore[arg-type]
70107046
axis=axis,
70117047
limit=limit,
70127048
inplace=inplace,
@@ -7111,7 +7147,10 @@ def fillna(
71117147
if axis == 1:
71127148
result = self.T.fillna(value=value, limit=limit).T
71137149

7114-
new_data = result
7150+
# error: Incompatible types in assignment (expression
7151+
# has type "Self", variable has type "Union[ArrayManager,
7152+
# SingleArrayManager, BlockManager, SingleBlockManager]")
7153+
new_data = result # type: ignore[assignment]
71157154
else:
71167155
new_data = self._mgr.fillna(
71177156
value=value, limit=limit, inplace=inplace, downcast=downcast
@@ -7180,6 +7219,25 @@ def ffill(
71807219
71817220
Examples
71827221
--------
7222+
>>> df = pd.DataFrame([[np.nan, 2, np.nan, 0],
7223+
... [3, 4, np.nan, 1],
7224+
... [np.nan, np.nan, np.nan, np.nan],
7225+
... [np.nan, 3, np.nan, 4]],
7226+
... columns=list("ABCD"))
7227+
>>> df
7228+
A B C D
7229+
0 NaN 2.0 NaN 0.0
7230+
1 3.0 4.0 NaN 1.0
7231+
2 NaN NaN NaN NaN
7232+
3 NaN 3.0 NaN 4.0
7233+
7234+
>>> df.ffill()
7235+
A B C D
7236+
0 NaN 2.0 NaN 0.0
7237+
1 3.0 4.0 NaN 1.0
7238+
2 3.0 4.0 NaN 1.0
7239+
3 3.0 3.0 NaN 4.0
7240+
71837241
>>> ser = pd.Series([1, np.NaN, 2, 3])
71847242
>>> ser.ffill()
71857243
0 1.0
@@ -7188,8 +7246,10 @@ def ffill(
71887246
3 3.0
71897247
dtype: float64
71907248
"""
7191-
return self.fillna(
7192-
method="ffill", axis=axis, inplace=inplace, limit=limit, downcast=downcast
7249+
self._deprecate_downcast(downcast)
7250+
7251+
return self._fillna_with_method(
7252+
"ffill", axis=axis, inplace=inplace, limit=limit, downcast=downcast
71937253
)
71947254

71957255
@final
@@ -7319,8 +7379,9 @@ def bfill(
73197379
2 4.0 7
73207380
3 4.0 7
73217381
"""
7322-
return self.fillna(
7323-
method="bfill", axis=axis, inplace=inplace, limit=limit, downcast=downcast
7382+
self._deprecate_downcast(downcast)
7383+
return self._fillna_with_method(
7384+
"bfill", axis=axis, inplace=inplace, limit=limit, downcast=downcast
73247385
)
73257386

73267387
@final
@@ -9840,8 +9901,8 @@ def _align_frame(
98409901
)
98419902

98429903
if method is not None:
9843-
left = left.fillna(method=method, axis=fill_axis, limit=limit)
9844-
right = right.fillna(method=method, axis=fill_axis, limit=limit)
9904+
left = left._fillna_with_method(method, axis=fill_axis, limit=limit)
9905+
right = right._fillna_with_method(method, axis=fill_axis, limit=limit)
98459906

98469907
return left, right, join_index
98479908

@@ -9916,8 +9977,13 @@ def _align_series(
99169977
# fill
99179978
fill_na = notna(fill_value) or (method is not None)
99189979
if fill_na:
9919-
left = left.fillna(fill_value, method=method, limit=limit, axis=fill_axis)
9920-
right = right.fillna(fill_value, method=method, limit=limit)
9980+
fill_value, method = validate_fillna_kwargs(fill_value, method)
9981+
if method is not None:
9982+
left = left._fillna_with_method(method, limit=limit, axis=fill_axis)
9983+
right = right._fillna_with_method(method, limit=limit)
9984+
else:
9985+
left = left.fillna(fill_value, limit=limit, axis=fill_axis)
9986+
right = right.fillna(fill_value, limit=limit)
99219987

99229988
return left, right, join_index
99239989

@@ -11283,9 +11349,7 @@ def pct_change(
1128311349
if fill_method is None:
1128411350
data = self
1128511351
else:
11286-
_data = self.fillna(method=fill_method, axis=axis, limit=limit)
11287-
assert _data is not None # needed for mypy
11288-
data = _data
11352+
data = self._fillna_with_method(fill_method, axis=axis, limit=limit)
1128911353

1129011354
shifted = data.shift(periods=periods, freq=freq, axis=axis, **kwargs)
1129111355
# Unsupported left operand type for / ("Self")

0 commit comments

Comments
 (0)