From c00f9818c49ce8124ea2e90a5459d79d224deb36 Mon Sep 17 00:00:00 2001 From: Luke Manley Date: Fri, 11 Mar 2022 19:14:15 -0500 Subject: [PATCH 1/5] bug: replace with value also being replaced --- pandas/core/internals/blocks.py | 12 +++++++++--- pandas/tests/frame/methods/test_replace.py | 7 +++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 3693edbae7d95..885643e4e194b 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -612,9 +612,15 @@ def replace( else: # split so that we only upcast where necessary - return self.split_and_operate( - type(self).replace, to_replace, value, inplace=True - ) + blocks = [] + nbs = self._split() + for i, nb in enumerate(nbs): + submask = mask[i] + rbs = type(self).replace( + nb, to_replace, value, inplace=True, mask=submask + ) + blocks.extend(rbs) + return blocks @final def _replace_regex( diff --git a/pandas/tests/frame/methods/test_replace.py b/pandas/tests/frame/methods/test_replace.py index 6b53ef400e53d..2eb300a8905b8 100644 --- a/pandas/tests/frame/methods/test_replace.py +++ b/pandas/tests/frame/methods/test_replace.py @@ -1542,3 +1542,10 @@ def test_replace_regex_dtype_frame(self, regex): expected_df2 = DataFrame({"A": [1], "B": ["1"]}) result_df2 = df2.replace(to_replace="0", value=1, regex=regex) tm.assert_frame_equal(result_df2, expected_df2) + + def test_replace_with_value_also_being_replaced(self): + # GH46306 + df = DataFrame({"A": [0, 1, 2], "B": [1, 0, 2]}) + result = df.replace({0: 1, 1: np.nan}) + expected = DataFrame({"A": [1, np.nan, 2], "B": [np.nan, 1, 2]}) + tm.assert_frame_equal(result, expected) From 75cc610a88968535c2879fbc32eec02456cfb335 Mon Sep 17 00:00:00 2001 From: Luke Manley Date: Fri, 11 Mar 2022 22:41:00 -0500 Subject: [PATCH 2/5] updates --- doc/source/whatsnew/v1.4.2.rst | 1 + pandas/core/internals/blocks.py | 13 ++++--------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/doc/source/whatsnew/v1.4.2.rst b/doc/source/whatsnew/v1.4.2.rst index 2bdbeb0ab6991..06f1f406c3816 100644 --- a/doc/source/whatsnew/v1.4.2.rst +++ b/doc/source/whatsnew/v1.4.2.rst @@ -18,6 +18,7 @@ Fixed regressions - Fixed regression in :func:`read_csv` killing python process when invalid file input was given for ``engine="c"`` (:issue:`45957`) - 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:`46335`) - Fixed regression in :meth:`DataFrame.loc.__setitem__` 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 885643e4e194b..8f536c1b642db 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -612,15 +612,10 @@ def replace( else: # split so that we only upcast where necessary - blocks = [] - nbs = self._split() - for i, nb in enumerate(nbs): - submask = mask[i] - rbs = type(self).replace( - nb, to_replace, value, inplace=True, mask=submask - ) - blocks.extend(rbs) - return blocks + return [ + type(self).replace(nb, to_replace, value, inplace=True, mask=mask[i]) + for i, nb in enumerate(self._split()) + ] @final def _replace_regex( From 6ab4e8fecd8d31d8f6a77987025a3c383b518a1b Mon Sep 17 00:00:00 2001 From: Luke Manley Date: Sat, 12 Mar 2022 06:27:43 -0500 Subject: [PATCH 3/5] mypy --- pandas/core/internals/blocks.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 8f536c1b642db..205352cbc4d76 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -612,10 +612,17 @@ def replace( else: # split so that we only upcast where necessary - return [ - type(self).replace(nb, to_replace, value, inplace=True, mask=mask[i]) - for i, nb in enumerate(self._split()) - ] + blocks = [] + for i, nb in enumerate(self._split()): + blocks.extend( + type(self).replace( + nb, + to_replace=to_replace, + value=value, + inplace=True, + mask=mask[i], + ) + ) @final def _replace_regex( From 411c6c37de7b9ecb8b6087804646d9d07fc0c0e6 Mon Sep 17 00:00:00 2001 From: Luke Manley Date: Sat, 12 Mar 2022 06:58:08 -0500 Subject: [PATCH 4/5] fixup --- pandas/core/internals/blocks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 205352cbc4d76..7a777bac06958 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -623,6 +623,7 @@ def replace( mask=mask[i], ) ) + return blocks @final def _replace_regex( From 57a33a621614655a61546ebc14daaf3c840c291d Mon Sep 17 00:00:00 2001 From: Luke Manley Date: Sat, 12 Mar 2022 08:13:45 -0500 Subject: [PATCH 5/5] fix mask indexer --- pandas/core/internals/blocks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 7a777bac06958..69f66973d0954 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -620,7 +620,7 @@ def replace( to_replace=to_replace, value=value, inplace=True, - mask=mask[i], + mask=mask[i : i + 1], ) ) return blocks