From d6099e58629a0f133f182ac5aad8bc47bba7d4e0 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Sat, 19 Mar 2022 18:26:21 +0000 Subject: [PATCH] Backport PR #46404: REGR: DataFrame.replace when the replacement value was explicitly None --- doc/source/whatsnew/v1.4.2.rst | 1 + pandas/core/internals/blocks.py | 7 +++++++ pandas/tests/frame/methods/test_replace.py | 14 ++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/doc/source/whatsnew/v1.4.2.rst b/doc/source/whatsnew/v1.4.2.rst index 3523f691cae8f..4cbb8118055af 100644 --- a/doc/source/whatsnew/v1.4.2.rst +++ b/doc/source/whatsnew/v1.4.2.rst @@ -19,6 +19,7 @@ Fixed regressions - Fixed memory performance regression in :meth:`Series.fillna` when called on a :class:`DataFrame` column with ``inplace=True`` (:issue:`46149`) - Provided an alternative solution for passing custom Excel formats in :meth:`.Styler.to_excel`, which was a regression based on stricter CSS validation. Examples available in the documentation for :meth:`.Styler.format` (:issue:`46152`) - Fixed regression in :meth:`DataFrame.replace` when a replacement value was also a target for replacement (:issue:`46306`) +- Fixed regression in :meth:`DataFrame.replace` when the replacement value was explicitly ``None`` when passed in a dictionary to ``to_replace`` (:issue:`45601`, :issue:`45836`) - Fixed regression when setting values with :meth:`DataFrame.loc` losing :class:`MultiIndex` names if :class:`DataFrame` was empty before (:issue:`46317`) - diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index e0df274fbc596..8a09e4ff2d5b7 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -874,6 +874,13 @@ def _replace_coerce( mask=mask, ) else: + if value is None: + # gh-45601, gh-45836 + nb = self.astype(np.dtype(object), copy=False) + if nb is self and not inplace: + nb = nb.copy() + putmask_inplace(nb.values, mask, value) + return [nb] return self.replace( to_replace=to_replace, value=value, inplace=inplace, mask=mask ) diff --git a/pandas/tests/frame/methods/test_replace.py b/pandas/tests/frame/methods/test_replace.py index 4393dbacc6fcf..b84af7b0e0b52 100644 --- a/pandas/tests/frame/methods/test_replace.py +++ b/pandas/tests/frame/methods/test_replace.py @@ -661,6 +661,20 @@ def test_replace_simple_nested_dict_with_nonexistent_value(self): result = df.replace({"col": {-1: "-", 1: "a", 4: "b"}}) tm.assert_frame_equal(expected, result) + def test_replace_NA_with_None(self): + # gh-45601 + df = DataFrame({"value": [42, None]}).astype({"value": "Int64"}) + result = df.replace({pd.NA: None}) + expected = DataFrame({"value": [42, None]}, dtype=object) + tm.assert_frame_equal(result, expected) + + def test_replace_NAT_with_None(self): + # gh-45836 + df = DataFrame([pd.NaT, pd.NaT]) + result = df.replace({pd.NaT: None, np.NaN: None}) + expected = DataFrame([None, None]) + tm.assert_frame_equal(result, expected) + def test_replace_value_is_none(self, datetime_frame): orig_value = datetime_frame.iloc[0, 0] orig2 = datetime_frame.iloc[1, 0]