From b639ded7505fa62c34935425876ca73dbc43eeff Mon Sep 17 00:00:00 2001 From: Patrick Hoefler Date: Tue, 20 Jun 2023 22:34:15 +0200 Subject: [PATCH] CoW: Series.transform not respecting CoW --- doc/source/whatsnew/v2.1.0.rst | 1 + pandas/core/series.py | 3 ++- pandas/tests/copy_view/test_methods.py | 26 ++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v2.1.0.rst b/doc/source/whatsnew/v2.1.0.rst index 44691e4265f5b..140e9d5b2205b 100644 --- a/doc/source/whatsnew/v2.1.0.rst +++ b/doc/source/whatsnew/v2.1.0.rst @@ -19,6 +19,7 @@ Enhancements Copy-on-Write improvements ^^^^^^^^^^^^^^^^^^^^^^^^^^ +- :meth:`Series.transform` not respecting Copy-on-Write when ``func`` modifies :class:`Series` inplace (:issue:`53747`) - Calling :meth:`Index.values` will now return a read-only NumPy array (:issue:`53704`) - Setting a :class:`Series` into a :class:`DataFrame` now creates a lazy instead of a deep copy (:issue:`53142`) - The :class:`DataFrame` constructor, when constructing a DataFrame from a dictionary diff --git a/pandas/core/series.py b/pandas/core/series.py index 959c153561572..40c19a57466fe 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -4499,7 +4499,8 @@ def transform( ) -> DataFrame | Series: # Validate axis argument self._get_axis_number(axis) - result = SeriesApply(self, func=func, args=args, kwargs=kwargs).transform() + ser = self.copy(deep=False) if using_copy_on_write() else self + result = SeriesApply(ser, func=func, args=args, kwargs=kwargs).transform() return result def apply( diff --git a/pandas/tests/copy_view/test_methods.py b/pandas/tests/copy_view/test_methods.py index 3af47d0b37338..294fd5636b7b5 100644 --- a/pandas/tests/copy_view/test_methods.py +++ b/pandas/tests/copy_view/test_methods.py @@ -1764,6 +1764,32 @@ def test_transpose_ea_single_column(using_copy_on_write): assert not np.shares_memory(get_array(df, "a"), get_array(result, 0)) +def test_transform_frame(using_copy_on_write): + df = DataFrame({"a": [1, 2, 3], "b": 1}) + df_orig = df.copy() + + def func(ser): + ser.iloc[0] = 100 + return ser + + df.transform(func) + if using_copy_on_write: + tm.assert_frame_equal(df, df_orig) + + +def test_transform_series(using_copy_on_write): + ser = Series([1, 2, 3]) + ser_orig = ser.copy() + + def func(ser): + ser.iloc[0] = 100 + return ser + + ser.transform(func) + if using_copy_on_write: + tm.assert_series_equal(ser, ser_orig) + + def test_count_read_only_array(): df = DataFrame({"a": [1, 2], "b": 3}) result = df.count()