Skip to content

Commit 6f58d86

Browse files
authored
BUG: replace with inplace not respecting cow (#51278)
1 parent 4a168d0 commit 6f58d86

File tree

4 files changed

+53
-2
lines changed

4 files changed

+53
-2
lines changed

doc/source/whatsnew/v2.0.0.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,9 @@ Copy-on-Write improvements
251251
can never update the original Series or DataFrame. Therefore, an informative
252252
error is raised to the user instead of silently doing nothing (:issue:`49467`)
253253

254+
- :meth:`DataFrame.replace` will now respect the Copy-on-Write mechanism
255+
when ``inplace=True``.
256+
254257
Copy-on-Write can be enabled through one of
255258

256259
.. code-block:: python

pandas/core/internals/blocks.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,7 @@ def replace_list(
673673
dest_list: Sequence[Any],
674674
inplace: bool = False,
675675
regex: bool = False,
676+
using_cow: bool = False,
676677
) -> list[Block]:
677678
"""
678679
See BlockManager.replace_list docstring.
@@ -682,7 +683,11 @@ def replace_list(
682683
if isinstance(values, Categorical):
683684
# TODO: avoid special-casing
684685
# GH49404
685-
blk = self if inplace else self.copy()
686+
if using_cow and inplace:
687+
# TODO(CoW): Optimize
688+
blk = self.copy()
689+
else:
690+
blk = self if inplace else self.copy()
686691
values = cast(Categorical, blk.values)
687692
values._replace(to_replace=src_list, value=dest_list, inplace=True)
688693
return [blk]
@@ -711,7 +716,11 @@ def replace_list(
711716

712717
masks = [extract_bool_array(x) for x in masks]
713718

714-
rb = [self if inplace else self.copy()]
719+
if using_cow and inplace:
720+
# TODO(CoW): Optimize
721+
rb = [self.copy()]
722+
else:
723+
rb = [self if inplace else self.copy()]
715724
for i, (src, dest) in enumerate(pairs):
716725
convert = i == src_len # only convert once at the end
717726
new_rb: list[Block] = []

pandas/core/internals/managers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ def replace_list(
468468
dest_list=dest_list,
469469
inplace=inplace,
470470
regex=regex,
471+
using_cow=using_copy_on_write(),
471472
)
472473
bm._consolidate_inplace()
473474
return bm
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import numpy as np
2+
3+
from pandas import (
4+
Categorical,
5+
DataFrame,
6+
)
7+
import pandas._testing as tm
8+
from pandas.tests.copy_view.util import get_array
9+
10+
11+
def test_replace_categorical_inplace_reference(using_copy_on_write):
12+
df = DataFrame({"a": Categorical([1, 2, 3])})
13+
df_orig = df.copy()
14+
arr_a = get_array(df, "a")
15+
view = df[:] # noqa
16+
df.replace(to_replace=[1], value=2, inplace=True)
17+
18+
if using_copy_on_write:
19+
assert not np.shares_memory(get_array(df, "a").codes, arr_a.codes)
20+
assert df._mgr._has_no_reference(0)
21+
assert view._mgr._has_no_reference(0)
22+
tm.assert_frame_equal(view, df_orig)
23+
else:
24+
assert np.shares_memory(get_array(df, "a").codes, arr_a.codes)
25+
26+
27+
def test_replace_inplace_reference(using_copy_on_write):
28+
df = DataFrame({"a": [1.5, 2, 3]})
29+
arr_a = get_array(df, "a")
30+
view = df[:] # noqa
31+
df.replace(to_replace=[1.5], value=15.5, inplace=True)
32+
33+
if using_copy_on_write:
34+
assert not np.shares_memory(get_array(df, "a"), arr_a)
35+
assert df._mgr._has_no_reference(0)
36+
assert view._mgr._has_no_reference(0)
37+
else:
38+
assert np.shares_memory(get_array(df, "a"), arr_a)

0 commit comments

Comments
 (0)