Repository: pandas-dev/pandas
Pull request #56381 (2023-12-07 19:20:30)

CoW: Warn for transform inplace modification
xref https://github.com/pandas-dev/pandas/issues/56019


Patch:
diff --git a/doc/source/whatsnew/v2.1.4.rst b/doc/source/whatsnew/v2.1.4.rst
index ee2d8efdbc..041b8fc866 100644
--- a/doc/source/whatsnew/v2.1.4.rst
+++ b/doc/source/whatsnew/v2.1.4.rst
@@ -32,6 +32,7 @@ Bug fixes
 - Fixed bug in :meth:`Index.insert` casting object-dtype to PyArrow backed strings when ``infer_string`` option is set (:issue:`55638`)
 - Fixed bug in :meth:`Series.__ne__` resulting in False for comparison between ``NA`` and string value for ``dtype="string[pyarrow_numpy]"`` (:issue:`56122`)
 - Fixed bug in :meth:`Series.mode` not keeping object dtype when ``infer_string`` is set (:issue:`56183`)
+- Fixed bug in :meth:`Series.reset_index` not preserving object dtype when ``infer_string`` is set (:issue:`56160`)
 - Fixed bug in :meth:`Series.str.split` and :meth:`Series.str.rsplit` when ``pat=None`` for :class:`ArrowDtype` with ``pyarrow.string`` (:issue:`56271`)
 - Fixed bug in :meth:`Series.str.translate` losing object dtype when string option is set (:issue:`56152`)
 
diff --git a/pandas/core/frame.py b/pandas/core/frame.py
index 1630c2c319..179279cc08 100644
--- a/pandas/core/frame.py
+++ b/pandas/core/frame.py
@@ -4895,7 +4895,6 @@ class DataFrame(NDFrame, OpsMixin):
 
         inplace = validate_bool_kwarg(inplace, "inplace")
         kwargs["level"] = kwargs.pop("level", 0) + 1
-        # TODO(CoW) those index/column resolvers create unnecessary refs to `self`
         index_resolvers = self._get_index_resolvers()
         column_resolvers = self._get_cleaned_column_resolvers()
         resolvers = column_resolvers, index_resolvers
diff --git a/pandas/core/series.py b/pandas/core/series.py
index 464e066b4e..e4dca97bc6 100644
--- a/pandas/core/series.py
+++ b/pandas/core/series.py
@@ -1719,7 +1719,7 @@ class Series(base.IndexOpsMixin, NDFrame):  # type: ignore[misc]
                 return new_ser.__finalize__(self, method="reset_index")
             else:
                 return self._constructor(
-                    self._values.copy(), index=new_index, copy=False
+                    self._values.copy(), index=new_index, copy=False, dtype=self.dtype
                 ).__finalize__(self, method="reset_index")
         elif inplace:
             raise TypeError(
@@ -4740,7 +4740,11 @@ class Series(base.IndexOpsMixin, NDFrame):  # type: ignore[misc]
     ) -> DataFrame | Series:
         # Validate axis argument
         self._get_axis_number(axis)
-        ser = self.copy(deep=False) if using_copy_on_write() else self
+        ser = (
+            self.copy(deep=False)
+            if using_copy_on_write() or warn_copy_on_write()
+            else self
+        )
         result = SeriesApply(ser, func=func, args=args, kwargs=kwargs).transform()
         return result
 


Test patch:
diff --git a/pandas/tests/copy_view/test_methods.py b/pandas/tests/copy_view/test_methods.py
index 1e3c95dbc1..558b483933 100644
--- a/pandas/tests/copy_view/test_methods.py
+++ b/pandas/tests/copy_view/test_methods.py
@@ -1938,8 +1938,8 @@ def test_transform_series(using_copy_on_write, warn_copy_on_write):
         ser.iloc[0] = 100
         return ser
 
-    # TODO(CoW-warn) should warn?
-    ser.transform(func)
+    with tm.assert_cow_warning(warn_copy_on_write):
+        ser.transform(func)
     if using_copy_on_write:
         tm.assert_series_equal(ser, ser_orig)
 
diff --git a/pandas/tests/series/methods/test_reset_index.py b/pandas/tests/series/methods/test_reset_index.py
index 634b8699a8..48e2608a10 100644
--- a/pandas/tests/series/methods/test_reset_index.py
+++ b/pandas/tests/series/methods/test_reset_index.py
@@ -11,6 +11,7 @@ from pandas import (
     RangeIndex,
     Series,
     date_range,
+    option_context,
 )
 import pandas._testing as tm
 
@@ -167,6 +168,14 @@ class TestResetIndex:
         expected = Series(range(2), name="old")
         tm.assert_series_equal(ser, expected)
 
+    def test_reset_index_drop_infer_string(self):
+        # GH#56160
+        pytest.importorskip("pyarrow")
+        ser = Series(["a", "b", "c"], dtype=object)
+        with option_context("future.infer_string", True):
+            result = ser.reset_index(drop=True)
+        tm.assert_series_equal(result, ser)
+
 
 @pytest.mark.parametrize(
     "array, dtype",