From 19c8e96f34c682e7f37e39a1eb3435d5edd4e590 Mon Sep 17 00:00:00 2001 From: Patrick Hoefler Date: Fri, 23 Dec 2022 18:43:28 +0100 Subject: [PATCH 1/5] Add cow mechanism to rename_axis --- pandas/core/generic.py | 21 ++++++++++++++++----- pandas/tests/copy_view/test_methods.py | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 7a40e30c0ae7a..036a93016b563 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -1207,7 +1207,12 @@ class name axes, kwargs = self._construct_axes_from_arguments( (), kwargs, sentinel=lib.no_default ) - copy = kwargs.pop("copy", True) + using_copy_on_write = config.get_option("mode.copy_on_write") + if using_copy_on_write and "copy" not in kwargs: + copy = None + else: + copy = kwargs.pop("copy", True) + inplace = kwargs.pop("inplace", False) axis = kwargs.pop("axis", 0) if axis is not None: @@ -1227,7 +1232,9 @@ class name is_list_like(mapper) and not is_dict_like(mapper) ) if non_mapper: - return self._set_axis_name(mapper, axis=axis, inplace=inplace) + return self._set_axis_name( + mapper, axis=axis, inplace=inplace, copy=copy + ) else: raise ValueError("Use `.rename` to alter labels with a mapper.") else: @@ -1246,13 +1253,15 @@ class name f = common.get_rename_function(v) curnames = self._get_axis(axis).names newnames = [f(name) for name in curnames] - result._set_axis_name(newnames, axis=axis, inplace=True) + result._set_axis_name(newnames, axis=axis, inplace=True, copy=copy) if not inplace: return result return None @final - def _set_axis_name(self, name, axis: Axis = 0, inplace: bool_t = False): + def _set_axis_name( + self, name, axis: Axis = 0, inplace: bool_t = False, copy: bool_t = True + ): """ Set the name(s) of the axis. @@ -1265,6 +1274,8 @@ def _set_axis_name(self, name, axis: Axis = 0, inplace: bool_t = False): and the value 1 or 'columns' specifies columns. inplace : bool, default False If `True`, do operation inplace and return None. + copy: + Whether to make a copy of the result. Returns ------- @@ -1306,7 +1317,7 @@ def _set_axis_name(self, name, axis: Axis = 0, inplace: bool_t = False): idx = self._get_axis(axis).set_names(name) inplace = validate_bool_kwarg(inplace, "inplace") - renamed = self if inplace else self.copy() + renamed = self if inplace else self.copy(deep=copy) if axis == 0: renamed.index = idx else: diff --git a/pandas/tests/copy_view/test_methods.py b/pandas/tests/copy_view/test_methods.py index f5c7b31e59bc5..84866c545b960 100644 --- a/pandas/tests/copy_view/test_methods.py +++ b/pandas/tests/copy_view/test_methods.py @@ -3,6 +3,7 @@ from pandas import ( DataFrame, + Index, MultiIndex, Series, ) @@ -356,3 +357,21 @@ def test_reorder_levels(using_copy_on_write): if using_copy_on_write: assert not np.shares_memory(get_array(df2, "a"), get_array(df, "a")) tm.assert_frame_equal(df, df_orig) + + +@pytest.mark.parametrize("copy_kwargs", [{"copy": True}, {}]) +@pytest.mark.parametrize("kwargs", [{"mapper": "test"}, {"index": "test"}]) +def test_rename_axis(using_copy_on_write, kwargs, copy_kwargs): + df = DataFrame({"a": [1, 2, 3, 4]}, index=Index([1, 2, 3, 4], name="a")) + df_orig = df.copy() + df2 = df.rename_axis(**kwargs, **copy_kwargs) + + if using_copy_on_write and copy_kwargs: + assert np.shares_memory(get_array(df2, "a"), get_array(df, "a")) + else: + assert not np.shares_memory(get_array(df2, "a"), get_array(df, "a")) + + df2.iloc[0, 0] = 0 + if using_copy_on_write: + assert not np.shares_memory(get_array(df2, "a"), get_array(df, "a")) + tm.assert_frame_equal(df, df_orig) From 629f7119033bdf4280be9a507cb7a8346aeabcc0 Mon Sep 17 00:00:00 2001 From: Patrick Hoefler Date: Fri, 23 Dec 2022 22:55:09 +0100 Subject: [PATCH 2/5] Fix tests and mypy --- pandas/conftest.py | 1 + pandas/core/generic.py | 4 +++- pandas/tests/copy_view/test_methods.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pandas/conftest.py b/pandas/conftest.py index 3b167d9ef4fe2..14c4e59778c9a 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -1903,6 +1903,7 @@ def using_copy_on_write() -> bool: """ Fixture to check if Copy-on-Write is enabled. """ + pd.options.mode.copy_on_write = True return pd.options.mode.copy_on_write and pd.options.mode.data_manager == "block" diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 036a93016b563..cd06a02816570 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -1208,6 +1208,8 @@ class name (), kwargs, sentinel=lib.no_default ) using_copy_on_write = config.get_option("mode.copy_on_write") + + copy: bool_t | None if using_copy_on_write and "copy" not in kwargs: copy = None else: @@ -1260,7 +1262,7 @@ class name @final def _set_axis_name( - self, name, axis: Axis = 0, inplace: bool_t = False, copy: bool_t = True + self, name, axis: Axis = 0, inplace: bool_t = False, copy: bool_t | None = True ): """ Set the name(s) of the axis. diff --git a/pandas/tests/copy_view/test_methods.py b/pandas/tests/copy_view/test_methods.py index 84866c545b960..cf8c7340d5602 100644 --- a/pandas/tests/copy_view/test_methods.py +++ b/pandas/tests/copy_view/test_methods.py @@ -366,7 +366,7 @@ def test_rename_axis(using_copy_on_write, kwargs, copy_kwargs): df_orig = df.copy() df2 = df.rename_axis(**kwargs, **copy_kwargs) - if using_copy_on_write and copy_kwargs: + if using_copy_on_write and not copy_kwargs: assert np.shares_memory(get_array(df2, "a"), get_array(df, "a")) else: assert not np.shares_memory(get_array(df2, "a"), get_array(df, "a")) From c6b54e4d7224c633efbc451c837bfee96db0e25e Mon Sep 17 00:00:00 2001 From: Patrick Hoefler Date: Fri, 23 Dec 2022 23:05:23 +0100 Subject: [PATCH 3/5] Remove option --- pandas/conftest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/conftest.py b/pandas/conftest.py index 14c4e59778c9a..3b167d9ef4fe2 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -1903,7 +1903,6 @@ def using_copy_on_write() -> bool: """ Fixture to check if Copy-on-Write is enabled. """ - pd.options.mode.copy_on_write = True return pd.options.mode.copy_on_write and pd.options.mode.data_manager == "block" From b9b47206ad2fd8d8036dd429c5c8ee9403d73566 Mon Sep 17 00:00:00 2001 From: Patrick Hoefler Date: Sat, 24 Dec 2022 11:40:41 +0100 Subject: [PATCH 4/5] Fix check --- pandas/core/generic.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index cd06a02816570..24cd7954be1e4 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -1207,10 +1207,9 @@ class name axes, kwargs = self._construct_axes_from_arguments( (), kwargs, sentinel=lib.no_default ) - using_copy_on_write = config.get_option("mode.copy_on_write") copy: bool_t | None - if using_copy_on_write and "copy" not in kwargs: + if "copy" not in kwargs: copy = None else: copy = kwargs.pop("copy", True) From e11f3d3a7c87d0c93a0f9b8c8b23d58402ae4530 Mon Sep 17 00:00:00 2001 From: Patrick Hoefler Date: Wed, 28 Dec 2022 20:52:57 +0100 Subject: [PATCH 5/5] Simplify --- pandas/core/generic.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 2a36fd87e650b..e86427b8cd29d 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -1209,12 +1209,7 @@ class name axes, kwargs = self._construct_axes_from_arguments( (), kwargs, sentinel=lib.no_default ) - - copy: bool_t | None - if "copy" not in kwargs: - copy = None - else: - copy = kwargs.pop("copy", True) + copy: bool_t | None = kwargs.pop("copy", None) inplace = kwargs.pop("inplace", False) axis = kwargs.pop("axis", 0)