Skip to content
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v2.1.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ Deprecations
- Deprecated making the functions in a list of functions given to :meth:`DataFrame.agg` attempt to operate on each element in the :class:`DataFrame` and only operate on the columns of the :class:`DataFrame` if the elementwise operations failed. To keep the current behavior, use :meth:`DataFrame.transform` instead. (:issue:`53325`)
- Deprecated passing a :class:`DataFrame` to :meth:`DataFrame.from_records`, use :meth:`DataFrame.set_index` or :meth:`DataFrame.drop` instead (:issue:`51353`)
- Deprecated silently dropping unrecognized timezones when parsing strings to datetimes (:issue:`18702`)
- Deprecated the "downcast" keyword in :meth:`Series.interpolate`, :meth:`DataFrame.interpolate`, :meth:`Series.fillna`, :meth:`DataFrame.fillna`, :meth:`Series.ffill`, :meth:`DataFrame.ffill`, :meth:`Series.bfill`, :meth:`DataFrame.bfill` (:issue:`40988`)
- Deprecated the ``axis`` keyword in :meth:`DataFrame.ewm`, :meth:`Series.ewm`, :meth:`DataFrame.rolling`, :meth:`Series.rolling`, :meth:`DataFrame.expanding`, :meth:`Series.expanding` (:issue:`51778`)
- Deprecated the ``axis`` keyword in :meth:`DataFrame.resample`, :meth:`Series.resample` (:issue:`51778`)
- Deprecated the behavior of :func:`concat` with both ``len(keys) != len(objs)``, in a future version this will raise instead of truncating to the shorter of the two sequences (:issue:`43485`)
Expand Down Expand Up @@ -281,7 +282,6 @@ Deprecations
- Deprecated :func:`value_counts`, use ``pd.Series(obj).value_counts()`` instead (:issue:`47862`)
- Deprecated :meth:`Series.first` and :meth:`DataFrame.first` (please create a mask and filter using ``.loc`` instead) (:issue:`45908`)
- Deprecated :meth:`Series.interpolate` and :meth:`DataFrame.interpolate` for object-dtype (:issue:`53631`)
- Deprecated allowing ``downcast`` keyword other than ``None``, ``False``, "infer", or a dict with these as values in :meth:`Series.fillna`, :meth:`DataFrame.fillna` (:issue:`40988`)
- Deprecated allowing arbitrary ``fill_value`` in :class:`SparseDtype`, in a future version the ``fill_value`` will need to be compatible with the ``dtype.subtype``, either a scalar that can be held by that subtype or ``NaN`` for integer or bool subtypes (:issue:`23124`)
- Deprecated behavior of :func:`assert_series_equal` and :func:`assert_frame_equal` considering NA-like values (e.g. ``NaN`` vs ``None`` as equivalent) (:issue:`52081`)
- Deprecated constructing :class:`SparseArray` from scalar data, pass a sequence instead (:issue:`53039`)
Expand Down
93 changes: 49 additions & 44 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -6804,25 +6804,18 @@ def convert_dtypes(
# ----------------------------------------------------------------------
# Filling NA's

def _deprecate_downcast(self, downcast) -> None:
if isinstance(downcast, dict):
# GH#40988
for dc in downcast.values():
if dc is not None and dc is not False and dc != "infer":
warnings.warn(
"downcast entries other than None, False, and 'infer' "
"are deprecated and will raise in a future version",
FutureWarning,
stacklevel=find_stack_level(),
)
elif downcast is not None and downcast is not False and downcast != "infer":
# GH#40988
def _deprecate_downcast(self, downcast, method_name: str):
# GH#40988
if downcast is not lib.no_default:
warnings.warn(
"downcast other than None, False, and 'infer' are deprecated "
"and will raise in a future version",
f"The 'downcast' keyword in {method_name} is deprecated and "
"will be removed in a future version.",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we suggest using to_numeric(downcast) here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, will update

FutureWarning,
stacklevel=find_stack_level(),
)
else:
downcast = None
return downcast

@final
def _fillna_with_method(
Expand Down Expand Up @@ -6911,7 +6904,7 @@ def fillna(
axis: Axis | None = None,
inplace: bool_t = False,
limit: int | None = None,
downcast: dict | None = None,
downcast: dict | None | lib.NoDefault = lib.no_default,
) -> Self | None:
"""
Fill NA/NaN values using the specified method.
Expand Down Expand Up @@ -7030,7 +7023,8 @@ def fillna(
stacklevel=find_stack_level(),
)

self._deprecate_downcast(downcast)
was_no_default = downcast is lib.no_default
downcast = self._deprecate_downcast(downcast, "fillna")

# set the default here, so functions examining the signaure
# can detect if something was set (e.g. in groupby) (GH9221)
Expand Down Expand Up @@ -7091,13 +7085,16 @@ def fillna(
if k not in result:
continue

# error: Item "None" of "Optional[Dict[Any, Any]]" has no
# attribute "get"
downcast_k = (
downcast
if not is_dict
else downcast.get(k) # type: ignore[union-attr]
)
if was_no_default:
downcast_k = lib.no_default
else:
# error: Item "None" of "Optional[Dict[Any, Any]]" has no
# attribute "get"
downcast_k = (
downcast
if not is_dict
else downcast.get(k) # type: ignore[union-attr]
)

res_k = result[k].fillna(v, limit=limit, downcast=downcast_k)

Expand Down Expand Up @@ -7174,7 +7171,7 @@ def ffill(
axis: None | Axis = ...,
inplace: Literal[False] = ...,
limit: None | int = ...,
downcast: dict | None = ...,
downcast: dict | None | lib.NoDefault = ...,
) -> Self:
...

Expand All @@ -7185,7 +7182,7 @@ def ffill(
axis: None | Axis = ...,
inplace: Literal[True],
limit: None | int = ...,
downcast: dict | None = ...,
downcast: dict | None | lib.NoDefault = ...,
) -> None:
...

Expand All @@ -7196,7 +7193,7 @@ def ffill(
axis: None | Axis = ...,
inplace: bool_t = ...,
limit: None | int = ...,
downcast: dict | None = ...,
downcast: dict | None | lib.NoDefault = ...,
) -> Self | None:
...

Expand All @@ -7208,7 +7205,7 @@ def ffill(
axis: None | Axis = None,
inplace: bool_t = False,
limit: None | int = None,
downcast: dict | None = None,
downcast: dict | None | lib.NoDefault = lib.no_default,
) -> Self | None:
"""
Synonym for :meth:`DataFrame.fillna` with ``method='ffill'``.
Expand Down Expand Up @@ -7247,7 +7244,7 @@ def ffill(
3 3.0
dtype: float64
"""
self._deprecate_downcast(downcast)
downcast = self._deprecate_downcast(downcast, "ffill")

return self._fillna_with_method(
"ffill", axis=axis, inplace=inplace, limit=limit, downcast=downcast
Expand All @@ -7261,7 +7258,7 @@ def pad(
axis: None | Axis = None,
inplace: bool_t = False,
limit: None | int = None,
downcast: dict | None = None,
downcast: dict | None | lib.NoDefault = lib.no_default,
) -> Self | None:
"""
Synonym for :meth:`DataFrame.fillna` with ``method='ffill'``.
Expand Down Expand Up @@ -7290,7 +7287,7 @@ def bfill(
axis: None | Axis = ...,
inplace: Literal[False] = ...,
limit: None | int = ...,
downcast: dict | None = ...,
downcast: dict | None | lib.NoDefault = ...,
) -> Self:
...

Expand All @@ -7301,7 +7298,7 @@ def bfill(
axis: None | Axis = ...,
inplace: Literal[True],
limit: None | int = ...,
downcast: dict | None = ...,
downcast: dict | None | lib.NoDefault = ...,
) -> None:
...

Expand All @@ -7312,7 +7309,7 @@ def bfill(
axis: None | Axis = ...,
inplace: bool_t = ...,
limit: None | int = ...,
downcast: dict | None = ...,
downcast: dict | None | lib.NoDefault = ...,
) -> Self | None:
...

Expand All @@ -7324,7 +7321,7 @@ def bfill(
axis: None | Axis = None,
inplace: bool_t = False,
limit: None | int = None,
downcast: dict | None = None,
downcast: dict | None | lib.NoDefault = lib.no_default,
) -> Self | None:
"""
Synonym for :meth:`DataFrame.fillna` with ``method='bfill'``.
Expand All @@ -7345,12 +7342,6 @@ def bfill(
2 2.0
3 2.0
dtype: float64
>>> s.bfill(downcast='infer')
0 1
1 2
2 2
3 2
dtype: int64
>>> s.bfill(limit=1)
0 1.0
1 NaN
Expand All @@ -7373,14 +7364,14 @@ def bfill(
1 4.0 5.0
2 4.0 7.0
3 4.0 7.0
>>> df.bfill(downcast='infer', limit=1)
>>> df.bfill(limit=1)
A B
0 1.0 5
1 NaN 5
2 4.0 7
3 4.0 7
"""
self._deprecate_downcast(downcast)
downcast = self._deprecate_downcast(downcast, "bfill")
return self._fillna_with_method(
"bfill", axis=axis, inplace=inplace, limit=limit, downcast=downcast
)
Expand All @@ -7393,7 +7384,7 @@ def backfill(
axis: None | Axis = None,
inplace: bool_t = False,
limit: None | int = None,
downcast: dict | None = None,
downcast: dict | None | lib.NoDefault = lib.no_default,
) -> Self | None:
"""
Synonym for :meth:`DataFrame.fillna` with ``method='bfill'``.
Expand Down Expand Up @@ -7685,7 +7676,7 @@ def interpolate(
inplace: bool_t = False,
limit_direction: Literal["forward", "backward", "both"] | None = None,
limit_area: Literal["inside", "outside"] | None = None,
downcast: Literal["infer"] | None = None,
downcast: Literal["infer"] | None | lib.NoDefault = lib.no_default,
**kwargs,
) -> Self | None:
"""
Expand Down Expand Up @@ -7756,6 +7747,9 @@ def interpolate(

downcast : optional, 'infer' or None, defaults to None
Downcast dtypes if possible.

.. deprecated:: 2.1.0

``**kwargs`` : optional
Keyword arguments to pass on to the interpolating function.

Expand Down Expand Up @@ -7854,6 +7848,17 @@ def interpolate(
3 16.0
Name: d, dtype: float64
"""
if downcast is not lib.no_default:
# GH#40988
warnings.warn(
f"The 'downcast' keyword in {type(self).__name__}.interpolate "
"is deprecated and will be removed in a future version. "
"Call result.infer_objects(copy=False) on the result instead.",
FutureWarning,
stacklevel=find_stack_level(),
)
else:
downcast = None
if downcast is not None and downcast != "infer":
raise ValueError("downcast must be either None or 'infer'")

Expand Down
8 changes: 6 additions & 2 deletions pandas/core/groupby/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@ def fillna(
axis: Axis | None | lib.NoDefault = lib.no_default,
inplace: bool = False,
limit: int | None = None,
downcast: dict | None = None,
downcast: dict | None | lib.NoDefault = lib.no_default,
) -> Series | None:
"""
Fill NA/NaN values using the specified method within groups.
Expand Down Expand Up @@ -905,6 +905,8 @@ def fillna(
or the string 'infer' which will try to downcast to an appropriate
equal type (e.g. float64 to int64 if possible).

.. deprecated:: 2.1.0

Returns
-------
Series
Expand Down Expand Up @@ -2362,7 +2364,7 @@ def fillna(
axis: Axis | None | lib.NoDefault = lib.no_default,
inplace: bool = False,
limit: int | None = None,
downcast=None,
downcast=lib.no_default,
) -> DataFrame | None:
"""
Fill NA/NaN values using the specified method within groups.
Expand Down Expand Up @@ -2406,6 +2408,8 @@ def fillna(
or the string 'infer' which will try to downcast to an appropriate
equal type (e.g. float64 to int64 if possible).

.. deprecated:: 2.1.0

Returns
-------
DataFrame
Expand Down
6 changes: 5 additions & 1 deletion pandas/core/resample.py
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,7 @@ def interpolate(
inplace: bool = False,
limit_direction: Literal["forward", "backward", "both"] = "forward",
limit_area=None,
downcast=None,
downcast=lib.no_default,
**kwargs,
):
"""
Expand Down Expand Up @@ -914,6 +914,9 @@ def interpolate(

downcast : optional, 'infer' or None, defaults to None
Downcast dtypes if possible.

.. deprecated::2.1.0

``**kwargs`` : optional
Keyword arguments to pass on to the interpolating function.

Expand Down Expand Up @@ -997,6 +1000,7 @@ def interpolate(
Note that the series erroneously increases between two anchors
``07:00:00`` and ``07:00:02``.
"""
assert downcast is lib.no_default # just checking coverage
result = self._upsample("asfreq")
return result.interpolate(
method=method,
Expand Down
12 changes: 8 additions & 4 deletions pandas/core/reshape/pivot.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,26 +207,30 @@ def __internal_pivot_table(
to_unstack.append(i)
else:
to_unstack.append(name)
table = agged.unstack(to_unstack)
table = agged.unstack(to_unstack, fill_value=fill_value)

if not dropna:
if isinstance(table.index, MultiIndex):
m = MultiIndex.from_arrays(
cartesian_product(table.index.levels), names=table.index.names
)
table = table.reindex(m, axis=0)
table = table.reindex(m, axis=0, fill_value=fill_value)

if isinstance(table.columns, MultiIndex):
m = MultiIndex.from_arrays(
cartesian_product(table.columns.levels), names=table.columns.names
)
table = table.reindex(m, axis=1)
table = table.reindex(m, axis=1, fill_value=fill_value)

if sort is True and isinstance(table, ABCDataFrame):
table = table.sort_index(axis=1)

if fill_value is not None:
table = table.fillna(fill_value, downcast="infer")
table = table.fillna(fill_value)
if aggfunc is len and not observed and lib.is_integer(fill_value):
# TODO: can we avoid this? this used to be handled by
# downcast="infer" in fillna
table = table.astype(np.int64)

if margins:
if dropna:
Expand Down
12 changes: 8 additions & 4 deletions pandas/tests/arithmetic/test_timedelta64.py
Original file line number Diff line number Diff line change
Expand Up @@ -2038,9 +2038,11 @@ def test_td64arr_div_numeric_array(
expected = [tdser.iloc[0, n] / vector[n] for n in range(len(vector))]
expected = tm.box_expected(expected, xbox).astype(object)
# We specifically expect timedelta64("NaT") here, not pd.NA
expected[2] = expected[2].fillna(
np.timedelta64("NaT", "ns"), downcast=False
)
msg = "The 'downcast' keyword in fillna"
with tm.assert_produces_warning(FutureWarning, match=msg):
expected[2] = expected[2].fillna(
np.timedelta64("NaT", "ns"), downcast=False
)
else:
expected = [tdser[n] / vector[n] for n in range(len(tdser))]
expected = [
Expand Down Expand Up @@ -2122,7 +2124,9 @@ def test_td64arr_all_nat_div_object_dtype_numeric(self, box_with_array):
if box_with_array is not Index:
expected = tm.box_expected(expected, box_with_array).astype(object)
if box_with_array in [Series, DataFrame]:
expected = expected.fillna(tdnat, downcast=False) # GH#18463
msg = "The 'downcast' keyword in fillna is deprecated"
with tm.assert_produces_warning(FutureWarning, match=msg):
expected = expected.fillna(tdnat, downcast=False) # GH#18463

result = left / right
tm.assert_equal(result, expected)
Expand Down
4 changes: 3 additions & 1 deletion pandas/tests/copy_view/test_interp_fillna.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@ def test_fillna_inplace(using_copy_on_write, downcast):
arr_a = get_array(df, "a")
arr_b = get_array(df, "b")

df.fillna(5.5, inplace=True, downcast=downcast)
msg = "The 'downcast' keyword in fillna is deprecated"
with tm.assert_produces_warning(FutureWarning, match=msg):
df.fillna(5.5, inplace=True, downcast=downcast)
assert np.shares_memory(get_array(df, "a"), arr_a)
assert np.shares_memory(get_array(df, "b"), arr_b)
if using_copy_on_write:
Expand Down
Loading