Skip to content

Commit efc3d95

Browse files
authored
DEPR: stricter downcast values in fillna (#53103)
1 parent 284cfb2 commit efc3d95

File tree

5 files changed

+56
-6
lines changed

5 files changed

+56
-6
lines changed

doc/source/whatsnew/v2.1.0.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ Deprecations
259259
- Deprecated unused "closed" and "normalize" keywords in the :class:`DatetimeIndex` constructor (:issue:`52628`)
260260
- Deprecated unused "closed" keyword in the :class:`TimedeltaIndex` constructor (:issue:`52628`)
261261
- Deprecated logical operation between two non boolean :class:`Series` with different indexes always coercing the result to bool dtype. In a future version, this will maintain the return type of the inputs. (:issue:`52500`, :issue:`52538`)
262+
- 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`)
262263
- Deprecated constructing :class:`SparseArray` from scalar data, pass a sequence instead (:issue:`53039`)
263264
-
264265

@@ -361,7 +362,7 @@ Indexing
361362

362363
Missing
363364
^^^^^^^
364-
-
365+
- Bug in :meth:`Series.interpolate` and :meth:`DataFrame.interpolate` failing to raise on invalid ``downcast`` keyword, which can be only ``None`` or "infer" (:issue:`53103`)
365366
-
366367

367368
MultiIndex

pandas/core/generic.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -6972,6 +6972,25 @@ def fillna(
69726972
inplace = validate_bool_kwarg(inplace, "inplace")
69736973
value, method = validate_fillna_kwargs(value, method)
69746974

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
6987+
warnings.warn(
6988+
"downcast other than None, False, and 'infer' are deprecated "
6989+
"and will raise in a future version",
6990+
FutureWarning,
6991+
stacklevel=find_stack_level(),
6992+
)
6993+
69756994
# set the default here, so functions examining the signaure
69766995
# can detect if something was set (e.g. in groupby) (GH9221)
69776996
if axis is None:
@@ -7548,7 +7567,7 @@ def interpolate(
75487567
inplace: bool_t = False,
75497568
limit_direction: Literal["forward", "backward", "both"] | None = None,
75507569
limit_area: Literal["inside", "outside"] | None = None,
7551-
downcast: str | None = None,
7570+
downcast: Literal["infer"] | None = None,
75527571
**kwargs,
75537572
) -> Self | None:
75547573
"""
@@ -7746,6 +7765,9 @@ def interpolate(
77467765
3 16.0
77477766
Name: d, dtype: float64
77487767
"""
7768+
if downcast is not None and downcast != "infer":
7769+
raise ValueError("downcast must be either None or 'infer'")
7770+
77497771
inplace = validate_bool_kwarg(inplace, "inplace")
77507772

77517773
axis = self._get_axis_number(axis)

pandas/core/internals/blocks.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Any,
88
Callable,
99
Iterable,
10+
Literal,
1011
Sequence,
1112
cast,
1213
final,
@@ -1323,7 +1324,7 @@ def interpolate(
13231324
limit_direction: str = "forward",
13241325
limit_area: str | None = None,
13251326
fill_value: Any | None = None,
1326-
downcast: str | None = None,
1327+
downcast: Literal["infer"] | None = None,
13271328
using_cow: bool = False,
13281329
**kwargs,
13291330
) -> list[Block]:

pandas/tests/frame/methods/test_fillna.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,11 @@ def test_fillna_downcast_noop(self, frame_or_series):
297297
# 2) _can_hold_na + noop + not can_hold_element
298298

299299
obj = frame_or_series([1, 2, 3], dtype=np.int64)
300-
res = obj.fillna("foo", downcast=np.dtype(np.int32))
300+
301+
msg = "downcast other than None, False, and 'infer' are deprecated"
302+
with tm.assert_produces_warning(FutureWarning, match=msg):
303+
# GH#40988
304+
res = obj.fillna("foo", downcast=np.dtype(np.int32))
301305
expected = obj.astype(np.int32)
302306
tm.assert_equal(res, expected)
303307

@@ -306,7 +310,9 @@ def test_fillna_downcast_noop(self, frame_or_series):
306310
expected2 = obj # get back int64
307311
tm.assert_equal(res2, expected2)
308312

309-
res3 = obj2.fillna("foo", downcast=np.dtype(np.int32))
313+
with tm.assert_produces_warning(FutureWarning, match=msg):
314+
# GH#40988
315+
res3 = obj2.fillna("foo", downcast=np.dtype(np.int32))
310316
tm.assert_equal(res3, expected)
311317

312318
@pytest.mark.parametrize("columns", [["A", "A", "B"], ["A", "A"]])
@@ -605,7 +611,10 @@ def test_fill_corner(self, float_frame, float_string_frame):
605611
def test_fillna_downcast_dict(self):
606612
# GH#40809
607613
df = DataFrame({"col1": [1, np.nan]})
608-
result = df.fillna({"col1": 2}, downcast={"col1": "int64"})
614+
615+
msg = "downcast entries other than None, False, and 'infer' are deprecated"
616+
with tm.assert_produces_warning(FutureWarning, match=msg):
617+
result = df.fillna({"col1": 2}, downcast={"col1": "int64"})
609618
expected = DataFrame({"col1": [1, 2]})
610619
tm.assert_frame_equal(result, expected)
611620

pandas/tests/frame/methods/test_interpolate.py

+17
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,23 @@ def test_interp_combo(self):
151151
expected = Series([1, 2, 3, 4], name="A")
152152
tm.assert_series_equal(result, expected)
153153

154+
def test_inerpolate_invalid_downcast(self):
155+
# GH#53103
156+
df = DataFrame(
157+
{
158+
"A": [1.0, 2.0, np.nan, 4.0],
159+
"B": [1, 4, 9, np.nan],
160+
"C": [1, 2, 3, 5],
161+
"D": list("abcd"),
162+
}
163+
)
164+
165+
msg = "downcast must be either None or 'infer'"
166+
with pytest.raises(ValueError, match=msg):
167+
df.interpolate(downcast="int64")
168+
with pytest.raises(ValueError, match=msg):
169+
df["A"].interpolate(downcast="int64")
170+
154171
def test_interp_nan_idx(self):
155172
df = DataFrame({"A": [1, 2, np.nan, 4], "B": [np.nan, 2, 3, 4]})
156173
df = df.set_index("A")

0 commit comments

Comments
 (0)