diff --git a/pandas/core/frame.py b/pandas/core/frame.py index fed92dc80a99b..83d14571b06e3 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -10916,7 +10916,7 @@ def to_timestamp( freq: Frequency | None = None, how: str = "start", axis: Axis = 0, - copy: bool = True, + copy: bool | None = None, ) -> DataFrame: """ Cast to DatetimeIndex of timestamps, at *beginning* of period. @@ -10951,7 +10951,7 @@ def to_timestamp( return new_obj def to_period( - self, freq: Frequency | None = None, axis: Axis = 0, copy: bool = True + self, freq: Frequency | None = None, axis: Axis = 0, copy: bool | None = None ) -> DataFrame: """ Convert DataFrame from DatetimeIndex to PeriodIndex. diff --git a/pandas/core/series.py b/pandas/core/series.py index b5d73373f061e..612e36c04c23a 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -5623,7 +5623,7 @@ def to_timestamp( self, freq=None, how: Literal["s", "e", "start", "end"] = "start", - copy: bool = True, + copy: bool | None = None, ) -> Series: """ Cast to DatetimeIndex of Timestamps, at *beginning* of period. @@ -5642,18 +5642,15 @@ def to_timestamp( ------- Series with DatetimeIndex """ - new_values = self._values - if copy: - new_values = new_values.copy() - if not isinstance(self.index, PeriodIndex): raise TypeError(f"unsupported Type {type(self.index).__name__}") + + new_obj = self.copy(deep=copy) new_index = self.index.to_timestamp(freq=freq, how=how) - return self._constructor(new_values, index=new_index).__finalize__( - self, method="to_timestamp" - ) + setattr(new_obj, "index", new_index) + return new_obj - def to_period(self, freq: str | None = None, copy: bool = True) -> Series: + def to_period(self, freq: str | None = None, copy: bool | None = None) -> Series: """ Convert Series from DatetimeIndex to PeriodIndex. @@ -5669,16 +5666,13 @@ def to_period(self, freq: str | None = None, copy: bool = True) -> Series: Series Series with index converted to PeriodIndex. """ - new_values = self._values - if copy: - new_values = new_values.copy() - if not isinstance(self.index, DatetimeIndex): raise TypeError(f"unsupported Type {type(self.index).__name__}") + + new_obj = self.copy(deep=copy) new_index = self.index.to_period(freq=freq) - return self._constructor(new_values, index=new_index).__finalize__( - self, method="to_period" - ) + setattr(new_obj, "index", new_index) + return new_obj @overload def ffill( diff --git a/pandas/tests/copy_view/test_methods.py b/pandas/tests/copy_view/test_methods.py index 933eea029a205..b41490f245d71 100644 --- a/pandas/tests/copy_view/test_methods.py +++ b/pandas/tests/copy_view/test_methods.py @@ -5,7 +5,9 @@ DataFrame, Index, MultiIndex, + Period, Series, + Timestamp, date_range, ) import pandas._testing as tm @@ -306,6 +308,42 @@ def test_chained_methods(request, method, idx, using_copy_on_write): tm.assert_frame_equal(df2.iloc[:, idx:], df_orig) +@pytest.mark.parametrize("obj", [Series([1, 2], name="a"), DataFrame({"a": [1, 2]})]) +def test_to_timestamp(using_copy_on_write, obj): + obj.index = Index([Period("2012-1-1", freq="D"), Period("2012-1-2", freq="D")]) + + obj_orig = obj.copy() + obj2 = obj.to_timestamp() + + if using_copy_on_write: + assert np.shares_memory(get_array(obj2, "a"), get_array(obj, "a")) + else: + assert not np.shares_memory(get_array(obj2, "a"), get_array(obj, "a")) + + # mutating obj2 triggers a copy-on-write for that column / block + obj2.iloc[0] = 0 + assert not np.shares_memory(get_array(obj2, "a"), get_array(obj, "a")) + tm.assert_equal(obj, obj_orig) + + +@pytest.mark.parametrize("obj", [Series([1, 2], name="a"), DataFrame({"a": [1, 2]})]) +def test_to_period(using_copy_on_write, obj): + obj.index = Index([Timestamp("2019-12-31"), Timestamp("2020-12-31")]) + + obj_orig = obj.copy() + obj2 = obj.to_period(freq="Y") + + if using_copy_on_write: + assert np.shares_memory(get_array(obj2, "a"), get_array(obj, "a")) + else: + assert not np.shares_memory(get_array(obj2, "a"), get_array(obj, "a")) + + # mutating obj2 triggers a copy-on-write for that column / block + obj2.iloc[0] = 0 + assert not np.shares_memory(get_array(obj2, "a"), get_array(obj, "a")) + tm.assert_equal(obj, obj_orig) + + def test_set_index(using_copy_on_write): # GH 49473 df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [0.1, 0.2, 0.3]}) diff --git a/pandas/tests/copy_view/util.py b/pandas/tests/copy_view/util.py index 0a111e6c6cca2..b5f4f74fc7e5e 100644 --- a/pandas/tests/copy_view/util.py +++ b/pandas/tests/copy_view/util.py @@ -1,17 +1,20 @@ +from pandas import Series from pandas.core.arrays import BaseMaskedArray -def get_array(df, col): +def get_array(obj, col): """ - Helper method to get array for a DataFrame column. + Helper method to get array for a DataFrame column or a Series. Equivalent of df[col].values, but without going through normal getitem, which triggers tracking references / CoW (and we might be testing that this is done by some other operation). """ - icol = df.columns.get_loc(col) + if isinstance(obj, Series) and obj.name == col: + return obj._values + icol = obj.columns.get_loc(col) assert isinstance(icol, int) - arr = df._get_column_array(icol) + arr = obj._get_column_array(icol) if isinstance(arr, BaseMaskedArray): return arr._data return arr