Skip to content

Commit 3b3162b

Browse files
Backport PR #46636 on branch 1.4.x (REGR: Replace changes the dtype of other columns) (#47123)
Backport PR #46636: REGR: Replace changes the dtype of other columns Co-authored-by: Simon Hawkins <[email protected]>
1 parent 4bb454f commit 3b3162b

File tree

3 files changed

+28
-6
lines changed

3 files changed

+28
-6
lines changed

doc/source/whatsnew/v1.4.3.rst

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ including other versions of pandas.
1414

1515
Fixed regressions
1616
~~~~~~~~~~~~~~~~~
17+
- Fixed regression in :meth:`DataFrame.replace` when the replacement value was explicitly ``None`` when passed in a dictionary to ``to_replace`` also casting other columns to object dtype even when there were no values to replace (:issue:`46634`)
1718
- Fixed regression in :meth:`DataFrame.nsmallest` led to wrong results when ``np.nan`` in the sorting column (:issue:`46589`)
1819
- Fixed regression in :func:`read_fwf` raising ``ValueError`` when ``widths`` was specified with ``usecols`` (:issue:`46580`)
1920
- Fixed regression in :meth:`.Groupby.transform` and :meth:`.Groupby.agg` failing with ``engine="numba"`` when the index was a :class:`MultiIndex` (:issue:`46867`)

pandas/core/internals/blocks.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -875,12 +875,14 @@ def _replace_coerce(
875875
)
876876
else:
877877
if value is None:
878-
# gh-45601, gh-45836
879-
nb = self.astype(np.dtype(object), copy=False)
880-
if nb is self and not inplace:
881-
nb = nb.copy()
882-
putmask_inplace(nb.values, mask, value)
883-
return [nb]
878+
# gh-45601, gh-45836, gh-46634
879+
if mask.any():
880+
nb = self.astype(np.dtype(object), copy=False)
881+
if nb is self and not inplace:
882+
nb = nb.copy()
883+
putmask_inplace(nb.values, mask, value)
884+
return [nb]
885+
return [self] if inplace else [self.copy()]
884886
return self.replace(
885887
to_replace=to_replace, value=value, inplace=inplace, mask=mask
886888
)

pandas/tests/frame/methods/test_replace.py

+19
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,25 @@ def test_replace_NAT_with_None(self):
675675
expected = DataFrame([None, None])
676676
tm.assert_frame_equal(result, expected)
677677

678+
def test_replace_with_None_keeps_categorical(self):
679+
# gh-46634
680+
cat_series = Series(["b", "b", "b", "d"], dtype="category")
681+
df = DataFrame(
682+
{
683+
"id": Series([5, 4, 3, 2], dtype="float64"),
684+
"col": cat_series,
685+
}
686+
)
687+
result = df.replace({3: None})
688+
689+
expected = DataFrame(
690+
{
691+
"id": Series([5.0, 4.0, None, 2.0], dtype="object"),
692+
"col": cat_series,
693+
}
694+
)
695+
tm.assert_frame_equal(result, expected)
696+
678697
def test_replace_value_is_none(self, datetime_frame):
679698
orig_value = datetime_frame.iloc[0, 0]
680699
orig2 = datetime_frame.iloc[1, 0]

0 commit comments

Comments
 (0)