Skip to content

Commit 94cfc4a

Browse files
Backport PR pandas-dev#51430 on branch 2.0.x (BUG: transpose not respecting CoW) (pandas-dev#51564)
Backport PR pandas-dev#51430: BUG: transpose not respecting CoW Co-authored-by: Patrick Hoefler <[email protected]>
1 parent 1070d3a commit 94cfc4a

File tree

4 files changed

+45
-2
lines changed

4 files changed

+45
-2
lines changed

doc/source/whatsnew/v2.0.0.rst

+2
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ Copy-on-Write improvements
261261
- :meth:`DataFrame.replace` will now respect the Copy-on-Write mechanism
262262
when ``inplace=True``.
263263

264+
- :meth:`DataFrame.transpose` will now respect the Copy-on-Write mechanism.
265+
264266
- Arithmetic operations that can be inplace, e.g. ``ser *= 2`` will now respect the
265267
Copy-on-Write mechanism.
266268

pandas/core/frame.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -3545,10 +3545,14 @@ def transpose(self, *args, copy: bool = False) -> DataFrame:
35453545
if self._can_fast_transpose:
35463546
# Note: tests pass without this, but this improves perf quite a bit.
35473547
new_vals = self._values.T
3548-
if copy:
3548+
if copy and not using_copy_on_write():
35493549
new_vals = new_vals.copy()
35503550

3551-
result = self._constructor(new_vals, index=self.columns, columns=self.index)
3551+
result = self._constructor(
3552+
new_vals, index=self.columns, columns=self.index, copy=False
3553+
)
3554+
if using_copy_on_write() and len(self) > 0:
3555+
result._mgr.add_references(self._mgr) # type: ignore[arg-type]
35523556

35533557
elif (
35543558
self._is_homogeneous_type and dtypes and is_extension_array_dtype(dtypes[0])

pandas/core/internals/managers.py

+3
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,9 @@ def add_references(self, mgr: BaseBlockManager) -> None:
253253
Adds the references from one manager to another. We assume that both
254254
managers have the same block structure.
255255
"""
256+
if len(self.blocks) != len(mgr.blocks):
257+
# If block structure changes, then we made a copy
258+
return
256259
for i, blk in enumerate(self.blocks):
257260
blk.refs = mgr.blocks[i].refs
258261
# Argument 1 to "add_reference" of "BlockValuesRefs" has incompatible type

pandas/tests/copy_view/test_methods.py

+34
Original file line numberDiff line numberDiff line change
@@ -1593,3 +1593,37 @@ def test_inplace_arithmetic_series_with_reference(using_copy_on_write):
15931593
tm.assert_series_equal(ser_orig, view)
15941594
else:
15951595
assert np.shares_memory(get_array(ser), get_array(view))
1596+
1597+
1598+
@pytest.mark.parametrize("copy", [True, False])
1599+
def test_transpose(using_copy_on_write, copy, using_array_manager):
1600+
df = DataFrame({"a": [1, 2, 3], "b": 1})
1601+
df_orig = df.copy()
1602+
result = df.transpose(copy=copy)
1603+
1604+
if not copy and not using_array_manager or using_copy_on_write:
1605+
assert np.shares_memory(get_array(df, "a"), get_array(result, 0))
1606+
else:
1607+
assert not np.shares_memory(get_array(df, "a"), get_array(result, 0))
1608+
1609+
result.iloc[0, 0] = 100
1610+
if using_copy_on_write:
1611+
tm.assert_frame_equal(df, df_orig)
1612+
1613+
1614+
def test_transpose_different_dtypes(using_copy_on_write):
1615+
df = DataFrame({"a": [1, 2, 3], "b": 1.5})
1616+
df_orig = df.copy()
1617+
result = df.T
1618+
1619+
assert not np.shares_memory(get_array(df, "a"), get_array(result, 0))
1620+
result.iloc[0, 0] = 100
1621+
if using_copy_on_write:
1622+
tm.assert_frame_equal(df, df_orig)
1623+
1624+
1625+
def test_transpose_ea_single_column(using_copy_on_write):
1626+
df = DataFrame({"a": [1, 2, 3]}, dtype="Int64")
1627+
result = df.T
1628+
1629+
assert not np.shares_memory(get_array(df, "a"), get_array(result, 0))

0 commit comments

Comments
 (0)