Skip to content

Commit 1496630

Browse files
DEPR: fillna downcasting from object dtype (#54261)
* DEPR: fillna downcasting from object dtype * GH ref * suppress warning * update test * Update doc/source/whatsnew/v2.1.0.rst Co-authored-by: Matthew Roeschke <[email protected]> --------- Co-authored-by: Matthew Roeschke <[email protected]>
1 parent 95b6057 commit 1496630

File tree

19 files changed

+113
-19
lines changed

19 files changed

+113
-19
lines changed

doc/source/whatsnew/v2.2.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ Deprecations
198198
- Deprecated strings ``T``, ``S``, ``L``, ``U``, and ``N`` denoting frequencies in :class:`Minute`, :class:`Second`, :class:`Milli`, :class:`Micro`, :class:`Nano` (:issue:`52536`)
199199
- Deprecated strings ``T``, ``S``, ``L``, ``U``, and ``N`` denoting units in :class:`Timedelta` (:issue:`52536`)
200200
- Deprecated the extension test classes ``BaseNoReduceTests``, ``BaseBooleanReduceTests``, and ``BaseNumericReduceTests``, use ``BaseReduceTests`` instead (:issue:`54663`)
201+
- Deprecating downcasting the results of :meth:`DataFrame.fillna`, :meth:`Series.fillna`, :meth:`DataFrame.ffill`, :meth:`Series.ffill`, :meth:`DataFrame.bfill`, :meth:`Series.bfill` in object-dtype cases. To opt in to the future version, use ``pd.set_option("future.no_silent_downcasting", True)`` (:issue:`54261`)
201202

202203
.. ---------------------------------------------------------------------------
203204
.. _whatsnew_220.performance:

pandas/core/generic.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10393,7 +10393,14 @@ def _where(
1039310393

1039410394
# make sure we are boolean
1039510395
fill_value = bool(inplace)
10396-
cond = cond.fillna(fill_value)
10396+
with warnings.catch_warnings():
10397+
warnings.filterwarnings(
10398+
"ignore",
10399+
"Downcasting object dtype arrays",
10400+
category=FutureWarning,
10401+
)
10402+
cond = cond.fillna(fill_value)
10403+
cond = cond.infer_objects(copy=False)
1039710404

1039810405
msg = "Boolean array expected for the condition, not {dtype}"
1039910406

pandas/core/internals/blocks.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,11 @@ def coerce_to_target_dtype(self, other, warn_on_upcast: bool = False) -> Block:
498498

499499
@final
500500
def _maybe_downcast(
501-
self, blocks: list[Block], downcast, using_cow: bool, caller: str
501+
self,
502+
blocks: list[Block],
503+
downcast,
504+
using_cow: bool,
505+
caller: str,
502506
) -> list[Block]:
503507
if downcast is False:
504508
return blocks
@@ -510,9 +514,29 @@ def _maybe_downcast(
510514
# but ATM it breaks too much existing code.
511515
# split and convert the blocks
512516

517+
if caller == "fillna" and get_option("future.no_silent_downcasting"):
518+
return blocks
519+
513520
nbs = extend_blocks(
514521
[blk.convert(using_cow=using_cow, copy=not using_cow) for blk in blocks]
515522
)
523+
if caller == "fillna":
524+
if len(nbs) != len(blocks) or not all(
525+
x.dtype == y.dtype for x, y in zip(nbs, blocks)
526+
):
527+
# GH#54261
528+
warnings.warn(
529+
"Downcasting object dtype arrays on .fillna, .ffill, .bfill "
530+
"is deprecated and will change in a future version. "
531+
"Call result.infer_objects(copy=False) instead. "
532+
"To opt-in to the future "
533+
"behavior, set "
534+
"`pd.set_option('future.no_silent_downcasting', True)`",
535+
FutureWarning,
536+
stacklevel=find_stack_level(),
537+
)
538+
539+
return nbs
516540

517541
elif downcast is None:
518542
return blocks
@@ -1549,7 +1573,7 @@ def pad_or_backfill(
15491573
data = extract_array(new_values, extract_numpy=True)
15501574

15511575
nb = self.make_block_same_class(data, refs=refs)
1552-
return nb._maybe_downcast([nb], downcast, using_cow, caller="pad_or_backfill")
1576+
return nb._maybe_downcast([nb], downcast, using_cow, caller="fillna")
15531577

15541578
@final
15551579
def interpolate(

pandas/io/formats/xml.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
TYPE_CHECKING,
1010
Any,
1111
)
12+
import warnings
1213

1314
from pandas.errors import AbstractMethodError
1415
from pandas.util._decorators import doc
@@ -202,7 +203,13 @@ def process_dataframe(self) -> dict[int | str, dict[str, Any]]:
202203
df = df.reset_index()
203204

204205
if self.na_rep is not None:
205-
df = df.fillna(self.na_rep)
206+
with warnings.catch_warnings():
207+
warnings.filterwarnings(
208+
"ignore",
209+
"Downcasting object dtype arrays",
210+
category=FutureWarning,
211+
)
212+
df = df.fillna(self.na_rep)
206213

207214
return df.to_dict(orient="index")
208215

pandas/io/json/_json.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,16 @@ def _try_convert_data(
12171217
if not self.dtype:
12181218
if all(notna(data)):
12191219
return data, False
1220-
return data.fillna(np.nan), True
1220+
1221+
with warnings.catch_warnings():
1222+
warnings.filterwarnings(
1223+
"ignore",
1224+
"Downcasting object dtype arrays",
1225+
category=FutureWarning,
1226+
)
1227+
filled = data.fillna(np.nan)
1228+
1229+
return filled, True
12211230

12221231
elif self.dtype is True:
12231232
pass

pandas/io/stata.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2983,7 +2983,14 @@ def _prepare_data(self) -> np.rec.recarray:
29832983
for i, col in enumerate(data):
29842984
typ = typlist[i]
29852985
if typ <= self._max_string_length:
2986-
data[col] = data[col].fillna("").apply(_pad_bytes, args=(typ,))
2986+
with warnings.catch_warnings():
2987+
warnings.filterwarnings(
2988+
"ignore",
2989+
"Downcasting object dtype arrays",
2990+
category=FutureWarning,
2991+
)
2992+
dc = data[col].fillna("")
2993+
data[col] = dc.apply(_pad_bytes, args=(typ,))
29872994
stype = f"S{typ}"
29882995
dtypes[col] = stype
29892996
data[col] = data[col].astype(stype)

pandas/plotting/_matplotlib/core.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1538,7 +1538,13 @@ def _kind(self) -> Literal["area"]:
15381538

15391539
def __init__(self, data, **kwargs) -> None:
15401540
kwargs.setdefault("stacked", True)
1541-
data = data.fillna(value=0)
1541+
with warnings.catch_warnings():
1542+
warnings.filterwarnings(
1543+
"ignore",
1544+
"Downcasting object dtype arrays",
1545+
category=FutureWarning,
1546+
)
1547+
data = data.fillna(value=0)
15421548
LinePlot.__init__(self, data, **kwargs)
15431549

15441550
if not self.stacked:

pandas/tests/extension/test_masked.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
be added to the array-specific tests in `pandas/tests/arrays/`.
1414
1515
"""
16+
import warnings
17+
1618
import numpy as np
1719
import pytest
1820

@@ -186,7 +188,14 @@ def _cast_pointwise_result(self, op_name: str, obj, other, pointwise_result):
186188

187189
if sdtype.kind in "iu":
188190
if op_name in ("__rtruediv__", "__truediv__", "__div__"):
189-
expected = expected.fillna(np.nan).astype("Float64")
191+
with warnings.catch_warnings():
192+
warnings.filterwarnings(
193+
"ignore",
194+
"Downcasting object dtype arrays",
195+
category=FutureWarning,
196+
)
197+
filled = expected.fillna(np.nan)
198+
expected = filled.astype("Float64")
190199
else:
191200
# combine method result in 'biggest' (int64) dtype
192201
expected = expected.astype(sdtype)

pandas/tests/frame/indexing/test_where.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ def test_where_upcasting(self):
9696

9797
tm.assert_series_equal(result, expected)
9898

99+
@pytest.mark.filterwarnings("ignore:Downcasting object dtype arrays:FutureWarning")
99100
def test_where_alignment(self, where_frame, float_string_frame):
100101
# aligning
101102
def _check_align(df, cond, other, check_dtypes=True):
@@ -170,6 +171,7 @@ def test_where_invalid(self):
170171
with pytest.raises(ValueError, match=msg):
171172
df.mask(0)
172173

174+
@pytest.mark.filterwarnings("ignore:Downcasting object dtype arrays:FutureWarning")
173175
def test_where_set(self, where_frame, float_string_frame, mixed_int_frame):
174176
# where inplace
175177

pandas/tests/frame/methods/test_fillna.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,9 @@ def test_fillna_dtype_conversion(self):
360360
expected = Series([np.dtype("object")] * 5, index=[1, 2, 3, 4, 5])
361361
tm.assert_series_equal(result, expected)
362362

363-
result = df.fillna(1)
363+
msg = "Downcasting object dtype arrays"
364+
with tm.assert_produces_warning(FutureWarning, match=msg):
365+
result = df.fillna(1)
364366
expected = DataFrame(1, index=["A", "B", "C"], columns=[1, 2, 3, 4, 5])
365367
tm.assert_frame_equal(result, expected)
366368

@@ -817,7 +819,8 @@ def test_fillna_nones_inplace():
817819
[[None, None], [None, None]],
818820
columns=["A", "B"],
819821
)
820-
with tm.assert_produces_warning(False):
822+
msg = "Downcasting object dtype arrays"
823+
with tm.assert_produces_warning(FutureWarning, match=msg):
821824
df.fillna(value={"A": 1, "B": 2}, inplace=True)
822825

823826
expected = DataFrame([[1, 2], [1, 2]], columns=["A", "B"])

0 commit comments

Comments
 (0)