Skip to content

Commit

Permalink
CoW: Add warning for replace with inplace (#56060)
Browse files Browse the repository at this point in the history
  • Loading branch information
phofl authored Nov 20, 2023
1 parent b2b0f97 commit 5625236
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 0 deletions.
17 changes: 17 additions & 0 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
SettingWithCopyError,
SettingWithCopyWarning,
_chained_assignment_method_msg,
_chained_assignment_warning_method_msg,
)
from pandas.util._decorators import (
deprecate_nonkeyword_arguments,
Expand Down Expand Up @@ -7773,6 +7774,22 @@ def replace(
ChainedAssignmentError,
stacklevel=2,
)
elif not PYPY and not using_copy_on_write():
ctr = sys.getrefcount(self)
ref_count = REF_COUNT
if isinstance(self, ABCSeries) and hasattr(self, "_cacher"):
# in non-CoW mode, chained Series access will populate the
# `_item_cache` which results in an increased ref count not below
# the threshold, while we still need to warn. We detect this case
# of a Series derived from a DataFrame through the presence of
# `_cacher`
ref_count += 1
if ctr <= ref_count:
warnings.warn(
_chained_assignment_warning_method_msg,
FutureWarning,
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")
Expand Down
13 changes: 13 additions & 0 deletions pandas/errors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,19 @@ class ChainedAssignmentError(Warning):
)


_chained_assignment_warning_method_msg = (
"A value is trying to be set on a copy of a DataFrame or Series "
"through chained assignment using an inplace method.\n"
"The behavior will change in pandas 3.0. This inplace method will "
"never work because the intermediate object on which we are setting "
"values always behaves as a copy.\n\n"
"For example, when doing 'df[col].method(value, inplace=True)', try "
"using 'df.method({col: value}, inplace=True)' or "
"df[col] = df[col].method(value) instead, to perform "
"the operation inplace on the original object.\n\n"
)


class NumExprClobberingError(NameError):
"""
Exception raised when trying to use a built-in numexpr name as a variable name.
Expand Down
12 changes: 12 additions & 0 deletions pandas/tests/copy_view/test_replace.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from pandas import (
Categorical,
DataFrame,
option_context,
)
import pandas._testing as tm
from pandas.tests.copy_view.util import get_array
Expand Down Expand Up @@ -395,6 +396,17 @@ def test_replace_chained_assignment(using_copy_on_write):
with tm.raises_chained_assignment_error():
df[["a"]].replace(1, 100, inplace=True)
tm.assert_frame_equal(df, df_orig)
else:
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
with option_context("mode.chained_assignment", None):
df[["a"]].replace(1, 100, inplace=True)

with tm.assert_produces_warning(FutureWarning, match="inplace method"):
with option_context("mode.chained_assignment", None):
df[df.a > 5].replace(1, 100, inplace=True)

with tm.assert_produces_warning(FutureWarning, match="inplace method"):
df["a"].replace(1, 100, inplace=True)


def test_replace_listlike(using_copy_on_write):
Expand Down
1 change: 1 addition & 0 deletions scripts/validate_unwanted_patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"_global_config",
"_chained_assignment_msg",
"_chained_assignment_method_msg",
"_chained_assignment_warning_method_msg",
"_version_meson",
# The numba extensions need this to mock the iloc object
"_iLocIndexer",
Expand Down

0 comments on commit 5625236

Please sign in to comment.