Skip to content

Commit cd0a4e6

Browse files
authored
CLN: Enforce deprecations for EA.fillna (#57983)
1 parent c900dc8 commit cd0a4e6

File tree

16 files changed

+38
-347
lines changed

16 files changed

+38
-347
lines changed

doc/source/whatsnew/v3.0.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ Other enhancements
3434
- Allow dictionaries to be passed to :meth:`pandas.Series.str.replace` via ``pat`` parameter (:issue:`51748`)
3535
- Support passing a :class:`Series` input to :func:`json_normalize` that retains the :class:`Series` :class:`Index` (:issue:`51452`)
3636
- Users can globally disable any ``PerformanceWarning`` by setting the option ``mode.performance_warnings`` to ``False`` (:issue:`56920`)
37-
-
3837

3938
.. ---------------------------------------------------------------------------
4039
.. _whatsnew_300.notable_bug_fixes:
@@ -258,6 +257,7 @@ Removal of prior version deprecations/changes
258257
- Unrecognized timezones when parsing strings to datetimes now raises a ``ValueError`` (:issue:`51477`)
259258
- Removed the :class:`Grouper` attributes ``ax``, ``groups``, ``indexer``, and ``obj`` (:issue:`51206`, :issue:`51182`)
260259
- Removed deprecated keyword ``verbose`` on :func:`read_csv` and :func:`read_table` (:issue:`56556`)
260+
- Removed the ``method`` keyword in ``ExtensionArray.fillna``, implement ``ExtensionArray._pad_or_backfill`` instead (:issue:`53621`)
261261
- Removed the attribute ``dtypes`` from :class:`.DataFrameGroupBy` (:issue:`51997`)
262262

263263
.. ---------------------------------------------------------------------------

pandas/core/arrays/_mixins.py

+6-26
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
from pandas.util._decorators import doc
3434
from pandas.util._validators import (
3535
validate_bool_kwarg,
36-
validate_fillna_kwargs,
3736
validate_insert_loc,
3837
)
3938

@@ -336,13 +335,7 @@ def _pad_or_backfill(
336335
return new_values
337336

338337
@doc(ExtensionArray.fillna)
339-
def fillna(
340-
self, value=None, method=None, limit: int | None = None, copy: bool = True
341-
) -> Self:
342-
value, method = validate_fillna_kwargs(
343-
value, method, validate_scalar_dict_value=False
344-
)
345-
338+
def fillna(self, value=None, limit: int | None = None, copy: bool = True) -> Self:
346339
mask = self.isna()
347340
# error: Argument 2 to "check_value_size" has incompatible type
348341
# "ExtensionArray"; expected "ndarray"
@@ -353,25 +346,12 @@ def fillna(
353346
)
354347

355348
if mask.any():
356-
if method is not None:
357-
# (for now) when self.ndim == 2, we assume axis=0
358-
func = missing.get_fill_func(method, ndim=self.ndim)
359-
npvalues = self._ndarray.T
360-
if copy:
361-
npvalues = npvalues.copy()
362-
func(npvalues, limit=limit, mask=mask.T)
363-
npvalues = npvalues.T
364-
365-
# TODO: NumpyExtensionArray didn't used to copy, need tests
366-
# for this
367-
new_values = self._from_backing_data(npvalues)
349+
# fill with value
350+
if copy:
351+
new_values = self.copy()
368352
else:
369-
# fill with value
370-
if copy:
371-
new_values = self.copy()
372-
else:
373-
new_values = self[:]
374-
new_values[mask] = value
353+
new_values = self[:]
354+
new_values[mask] = value
375355
else:
376356
# We validate the fill_value even if there is nothing to fill
377357
if value is not None:

pandas/core/arrays/arrow/array.py

+3-9
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
pa_version_under13p0,
3030
)
3131
from pandas.util._decorators import doc
32-
from pandas.util._validators import validate_fillna_kwargs
3332

3433
from pandas.core.dtypes.cast import (
3534
can_hold_element,
@@ -1068,6 +1067,7 @@ def _pad_or_backfill(
10681067
# a kernel for duration types.
10691068
pass
10701069

1070+
# TODO: Why do we no longer need the above cases?
10711071
# TODO(3.0): after EA.fillna 'method' deprecation is enforced, we can remove
10721072
# this method entirely.
10731073
return super()._pad_or_backfill(
@@ -1078,21 +1078,15 @@ def _pad_or_backfill(
10781078
def fillna(
10791079
self,
10801080
value: object | ArrayLike | None = None,
1081-
method: FillnaOptions | None = None,
10821081
limit: int | None = None,
10831082
copy: bool = True,
10841083
) -> Self:
1085-
value, method = validate_fillna_kwargs(value, method)
1086-
10871084
if not self._hasna:
10881085
# TODO(CoW): Not necessary anymore when CoW is the default
10891086
return self.copy()
10901087

10911088
if limit is not None:
1092-
return super().fillna(value=value, method=method, limit=limit, copy=copy)
1093-
1094-
if method is not None:
1095-
return super().fillna(method=method, limit=limit, copy=copy)
1089+
return super().fillna(value=value, limit=limit, copy=copy)
10961090

10971091
if isinstance(value, (np.ndarray, ExtensionArray)):
10981092
# Similar to check_value_size, but we do not mask here since we may
@@ -1118,7 +1112,7 @@ def fillna(
11181112
# a kernel for duration types.
11191113
pass
11201114

1121-
return super().fillna(value=value, method=method, limit=limit, copy=copy)
1115+
return super().fillna(value=value, limit=limit, copy=copy)
11221116

11231117
def isin(self, values: ArrayLike) -> npt.NDArray[np.bool_]:
11241118
# short-circuit to return all False array.

pandas/core/arrays/base.py

+6-66
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
from pandas.util._exceptions import find_stack_level
3939
from pandas.util._validators import (
4040
validate_bool_kwarg,
41-
validate_fillna_kwargs,
4241
validate_insert_loc,
4342
)
4443

@@ -1007,31 +1006,6 @@ def _pad_or_backfill(
10071006
[<NA>, 2, 2, 3, <NA>, <NA>]
10081007
Length: 6, dtype: Int64
10091008
"""
1010-
1011-
# If a 3rd-party EA has implemented this functionality in fillna,
1012-
# we warn that they need to implement _pad_or_backfill instead.
1013-
if (
1014-
type(self).fillna is not ExtensionArray.fillna
1015-
and type(self)._pad_or_backfill is ExtensionArray._pad_or_backfill
1016-
):
1017-
# Check for _pad_or_backfill here allows us to call
1018-
# super()._pad_or_backfill without getting this warning
1019-
warnings.warn(
1020-
"ExtensionArray.fillna 'method' keyword is deprecated. "
1021-
"In a future version. arr._pad_or_backfill will be called "
1022-
"instead. 3rd-party ExtensionArray authors need to implement "
1023-
"_pad_or_backfill.",
1024-
DeprecationWarning,
1025-
stacklevel=find_stack_level(),
1026-
)
1027-
if limit_area is not None:
1028-
raise NotImplementedError(
1029-
f"{type(self).__name__} does not implement limit_area "
1030-
"(added in pandas 2.2). 3rd-party ExtnsionArray authors "
1031-
"need to add this argument to _pad_or_backfill."
1032-
)
1033-
return self.fillna(method=method, limit=limit)
1034-
10351009
mask = self.isna()
10361010

10371011
if mask.any():
@@ -1057,8 +1031,7 @@ def _pad_or_backfill(
10571031

10581032
def fillna(
10591033
self,
1060-
value: object | ArrayLike | None = None,
1061-
method: FillnaOptions | None = None,
1034+
value: object | ArrayLike,
10621035
limit: int | None = None,
10631036
copy: bool = True,
10641037
) -> Self:
@@ -1071,24 +1044,13 @@ def fillna(
10711044
If a scalar value is passed it is used to fill all missing values.
10721045
Alternatively, an array-like "value" can be given. It's expected
10731046
that the array-like have the same length as 'self'.
1074-
method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
1075-
Method to use for filling holes in reindexed Series:
1076-
1077-
* pad / ffill: propagate last valid observation forward to next valid.
1078-
* backfill / bfill: use NEXT valid observation to fill gap.
1079-
1080-
.. deprecated:: 2.1.0
1081-
10821047
limit : int, default None
10831048
If method is specified, this is the maximum number of consecutive
10841049
NaN values to forward/backward fill. In other words, if there is
10851050
a gap with more than this number of consecutive NaNs, it will only
10861051
be partially filled. If method is not specified, this is the
10871052
maximum number of entries along the entire axis where NaNs will be
10881053
filled.
1089-
1090-
.. deprecated:: 2.1.0
1091-
10921054
copy : bool, default True
10931055
Whether to make a copy of the data before filling. If False, then
10941056
the original should be modified and no new memory should be allocated.
@@ -1110,16 +1072,6 @@ def fillna(
11101072
[0, 0, 2, 3, 0, 0]
11111073
Length: 6, dtype: Int64
11121074
"""
1113-
if method is not None:
1114-
warnings.warn(
1115-
f"The 'method' keyword in {type(self).__name__}.fillna is "
1116-
"deprecated and will be removed in a future version.",
1117-
FutureWarning,
1118-
stacklevel=find_stack_level(),
1119-
)
1120-
1121-
value, method = validate_fillna_kwargs(value, method)
1122-
11231075
mask = self.isna()
11241076
# error: Argument 2 to "check_value_size" has incompatible type
11251077
# "ExtensionArray"; expected "ndarray"
@@ -1130,24 +1082,12 @@ def fillna(
11301082
)
11311083

11321084
if mask.any():
1133-
if method is not None:
1134-
meth = missing.clean_fill_method(method)
1135-
1136-
npmask = np.asarray(mask)
1137-
if meth == "pad":
1138-
indexer = libalgos.get_fill_indexer(npmask, limit=limit)
1139-
return self.take(indexer, allow_fill=True)
1140-
else:
1141-
# i.e. meth == "backfill"
1142-
indexer = libalgos.get_fill_indexer(npmask[::-1], limit=limit)[::-1]
1143-
return self[::-1].take(indexer, allow_fill=True)
1085+
# fill with value
1086+
if not copy:
1087+
new_values = self[:]
11441088
else:
1145-
# fill with value
1146-
if not copy:
1147-
new_values = self[:]
1148-
else:
1149-
new_values = self.copy()
1150-
new_values[mask] = value
1089+
new_values = self.copy()
1090+
new_values[mask] = value
11511091
else:
11521092
if not copy:
11531093
new_values = self[:]

pandas/core/arrays/interval.py

+1-23
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
ArrayLike,
3030
AxisInt,
3131
Dtype,
32-
FillnaOptions,
3332
IntervalClosedType,
3433
NpDtype,
3534
PositionalIndexer,
@@ -894,23 +893,7 @@ def max(self, *, axis: AxisInt | None = None, skipna: bool = True) -> IntervalOr
894893
indexer = obj.argsort()[-1]
895894
return obj[indexer]
896895

897-
def _pad_or_backfill( # pylint: disable=useless-parent-delegation
898-
self,
899-
*,
900-
method: FillnaOptions,
901-
limit: int | None = None,
902-
limit_area: Literal["inside", "outside"] | None = None,
903-
copy: bool = True,
904-
) -> Self:
905-
# TODO(3.0): after EA.fillna 'method' deprecation is enforced, we can remove
906-
# this method entirely.
907-
return super()._pad_or_backfill(
908-
method=method, limit=limit, limit_area=limit_area, copy=copy
909-
)
910-
911-
def fillna(
912-
self, value=None, method=None, limit: int | None = None, copy: bool = True
913-
) -> Self:
896+
def fillna(self, value=None, limit: int | None = None, copy: bool = True) -> Self:
914897
"""
915898
Fill NA/NaN values using the specified method.
916899
@@ -921,9 +904,6 @@ def fillna(
921904
Alternatively, a Series or dict can be used to fill in different
922905
values for each index. The value should not be a list. The
923906
value(s) passed should be either Interval objects or NA/NaN.
924-
method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
925-
(Not implemented yet for IntervalArray)
926-
Method to use for filling holes in reindexed Series
927907
limit : int, default None
928908
(Not implemented yet for IntervalArray)
929909
If method is specified, this is the maximum number of consecutive
@@ -944,8 +924,6 @@ def fillna(
944924
"""
945925
if copy is False:
946926
raise NotImplementedError
947-
if method is not None:
948-
return super().fillna(value=value, method=method, limit=limit)
949927

950928
value_left, value_right = self._validate_scalar(value)
951929

pandas/core/arrays/masked.py

+6-21
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
)
3939
from pandas.errors import AbstractMethodError
4040
from pandas.util._decorators import doc
41-
from pandas.util._validators import validate_fillna_kwargs
4241

4342
from pandas.core.dtypes.base import ExtensionDtype
4443
from pandas.core.dtypes.common import (
@@ -237,32 +236,18 @@ def _pad_or_backfill(
237236
return new_values
238237

239238
@doc(ExtensionArray.fillna)
240-
def fillna(
241-
self, value=None, method=None, limit: int | None = None, copy: bool = True
242-
) -> Self:
243-
value, method = validate_fillna_kwargs(value, method)
244-
239+
def fillna(self, value=None, limit: int | None = None, copy: bool = True) -> Self:
245240
mask = self._mask
246241

247242
value = missing.check_value_size(value, mask, len(self))
248243

249244
if mask.any():
250-
if method is not None:
251-
func = missing.get_fill_func(method, ndim=self.ndim)
252-
npvalues = self._data.T
253-
new_mask = mask.T
254-
if copy:
255-
npvalues = npvalues.copy()
256-
new_mask = new_mask.copy()
257-
func(npvalues, limit=limit, mask=new_mask)
258-
return self._simple_new(npvalues.T, new_mask.T)
245+
# fill with value
246+
if copy:
247+
new_values = self.copy()
259248
else:
260-
# fill with value
261-
if copy:
262-
new_values = self.copy()
263-
else:
264-
new_values = self[:]
265-
new_values[mask] = value
249+
new_values = self[:]
250+
new_values[mask] = value
266251
else:
267252
if copy:
268253
new_values = self.copy()

pandas/core/arrays/period.py

-13
Original file line numberDiff line numberDiff line change
@@ -847,19 +847,6 @@ def _pad_or_backfill(
847847
else:
848848
return self
849849

850-
def fillna(
851-
self, value=None, method=None, limit: int | None = None, copy: bool = True
852-
) -> Self:
853-
if method is not None:
854-
# view as dt64 so we get treated as timelike in core.missing,
855-
# similar to dtl._period_dispatch
856-
dta = self.view("M8[ns]")
857-
result = dta.fillna(value=value, method=method, limit=limit, copy=copy)
858-
# error: Incompatible return value type (got "Union[ExtensionArray,
859-
# ndarray[Any, Any]]", expected "PeriodArray")
860-
return result.view(self.dtype) # type: ignore[return-value]
861-
return super().fillna(value=value, method=method, limit=limit, copy=copy)
862-
863850
# ------------------------------------------------------------------
864851
# Arithmetic Methods
865852

0 commit comments

Comments
 (0)