diff --git a/doc/source/whatsnew/v2.1.0.rst b/doc/source/whatsnew/v2.1.0.rst index 44e091e12bfa6..cf7285f94b218 100644 --- a/doc/source/whatsnew/v2.1.0.rst +++ b/doc/source/whatsnew/v2.1.0.rst @@ -75,6 +75,7 @@ Copy-on-Write improvements - DataFrame.update / Series.update - DataFrame.fillna / Series.fillna + - DataFrame.replace / Series.replace .. _whatsnew_210.enhancements.enhancement2: diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 9084395871675..7fe3a1a9c90a4 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -7623,6 +7623,15 @@ def replace( ) inplace = validate_bool_kwarg(inplace, "inplace") + if inplace: + if not PYPY and using_copy_on_write(): + if sys.getrefcount(self) <= REF_COUNT: + warnings.warn( + _chained_assignment_method_msg, + ChainedAssignmentError, + stacklevel=2, + ) + if not is_bool(regex) and to_replace is not None: raise ValueError("'to_replace' must be 'None' if 'regex' is not a bool") diff --git a/pandas/tests/arrays/categorical/test_replace.py b/pandas/tests/arrays/categorical/test_replace.py index d38f0b8719de0..0611d04d36d10 100644 --- a/pandas/tests/arrays/categorical/test_replace.py +++ b/pandas/tests/arrays/categorical/test_replace.py @@ -68,7 +68,8 @@ def test_replace_categorical(to_replace, value, result, expected_error_msg): # ensure non-inplace call does not affect original tm.assert_categorical_equal(cat, expected) - pd.Series(cat, copy=False).replace(to_replace, value, inplace=True) + ser = pd.Series(cat, copy=False) + ser.replace(to_replace, value, inplace=True) tm.assert_categorical_equal(cat, expected) diff --git a/pandas/tests/copy_view/test_replace.py b/pandas/tests/copy_view/test_replace.py index dfb1caa4b2ffd..4e80f8ed6d42a 100644 --- a/pandas/tests/copy_view/test_replace.py +++ b/pandas/tests/copy_view/test_replace.py @@ -373,3 +373,16 @@ def test_replace_columnwise_no_op(using_copy_on_write): assert not np.shares_memory(get_array(df2, "a"), get_array(df, "a")) df2.iloc[0, 0] = 100 tm.assert_frame_equal(df, df_orig) + + +def test_replace_chained_assignment(using_copy_on_write): + df = DataFrame({"a": [1, np.nan, 2], "b": 1}) + df_orig = df.copy() + if using_copy_on_write: + with tm.raises_chained_assignment_error(): + df["a"].replace(1, 100, inplace=True) + tm.assert_frame_equal(df, df_orig) + + with tm.raises_chained_assignment_error(): + df[["a"]].replace(1, 100, inplace=True) + tm.assert_frame_equal(df, df_orig)