Skip to content

Commit 3005094

Browse files
authored
API/CoW: Return copies for head and tail (#54011)
1 parent 2f08999 commit 3005094

File tree

3 files changed

+13
-3
lines changed

3 files changed

+13
-3
lines changed

doc/source/whatsnew/v2.1.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ Copy-on-Write improvements
6565
- The :class:`DataFrame` constructor, when constructing a DataFrame from a dictionary
6666
of Index objects and specifying ``copy=False``, will now use a lazy copy
6767
of those Index objects for the columns of the DataFrame (:issue:`52947`)
68+
- :meth:`DataFrame.head` and :meth:`DataFrame.tail` will now return deep copies (:issue:`54011`)
6869
- Add lazy copy mechanism to :meth:`DataFrame.eval` (:issue:`53746`)
6970

7071
- Trying to operate inplace on a temporary column selection

pandas/core/generic.py

+6
Original file line numberDiff line numberDiff line change
@@ -5794,6 +5794,8 @@ def head(self, n: int = 5) -> Self:
57945794
4 monkey
57955795
5 parrot
57965796
"""
5797+
if using_copy_on_write():
5798+
return self.iloc[:n].copy()
57975799
return self.iloc[:n]
57985800

57995801
@final
@@ -5869,6 +5871,10 @@ def tail(self, n: int = 5) -> Self:
58695871
7 whale
58705872
8 zebra
58715873
"""
5874+
if using_copy_on_write():
5875+
if n == 0:
5876+
return self.iloc[0:0].copy()
5877+
return self.iloc[-n:].copy()
58725878
if n == 0:
58735879
return self.iloc[0:0]
58745880
return self.iloc[-n:]

pandas/tests/copy_view/test_methods.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -895,16 +895,19 @@ def test_head_tail(method, using_copy_on_write):
895895
df2._mgr._verify_integrity()
896896

897897
if using_copy_on_write:
898-
assert np.shares_memory(get_array(df2, "a"), get_array(df, "a"))
899-
assert np.shares_memory(get_array(df2, "b"), get_array(df, "b"))
898+
# We are explicitly deviating for CoW here to make an eager copy (avoids
899+
# tracking references for very cheap ops)
900+
assert not np.shares_memory(get_array(df2, "a"), get_array(df, "a"))
901+
assert not np.shares_memory(get_array(df2, "b"), get_array(df, "b"))
900902

901903
# modify df2 to trigger CoW for that block
902904
df2.iloc[0, 0] = 0
903-
assert np.shares_memory(get_array(df2, "b"), get_array(df, "b"))
904905
if using_copy_on_write:
906+
assert not np.shares_memory(get_array(df2, "b"), get_array(df, "b"))
905907
assert not np.shares_memory(get_array(df2, "a"), get_array(df, "a"))
906908
else:
907909
# without CoW enabled, head and tail return views. Mutating df2 also mutates df.
910+
assert np.shares_memory(get_array(df2, "b"), get_array(df, "b"))
908911
df2.iloc[0, 0] = 1
909912
tm.assert_frame_equal(df, df_orig)
910913

0 commit comments

Comments
 (0)