Repository: pandas-dev/pandas
Pull request #57059 (2024-01-24 22:37:47)

Backport PR #57058 on branch 2.2.x (BUG: Series.pct_change with empty Series)
Backport PR #57058: BUG: Series.pct_change with empty Series


Patch:
diff --git a/doc/source/whatsnew/v2.2.1.rst b/doc/source/whatsnew/v2.2.1.rst
index 75445c978d..d3065cdd8b 100644
--- a/doc/source/whatsnew/v2.2.1.rst
+++ b/doc/source/whatsnew/v2.2.1.rst
@@ -14,6 +14,7 @@ including other versions of pandas.
 Fixed regressions
 ~~~~~~~~~~~~~~~~~
 - Fixed regression in :func:`merge_ordered` raising ``TypeError`` for ``fill_method="ffill"`` and ``how="left"`` (:issue:`57010`)
+- Fixed regression in :meth:`Series.pct_change` raising a ``ValueError`` for an empty :class:`Series` (:issue:`57056`)
 
 .. ---------------------------------------------------------------------------
 .. _whatsnew_221.bug_fixes:
diff --git a/pandas/compat/numpy/function.py b/pandas/compat/numpy/function.py
index a36e25a9df..4df30f7f4a 100644
--- a/pandas/compat/numpy/function.py
+++ b/pandas/compat/numpy/function.py
@@ -138,6 +138,7 @@ ARGSORT_DEFAULTS["axis"] = -1
 ARGSORT_DEFAULTS["kind"] = "quicksort"
 ARGSORT_DEFAULTS["order"] = None
 ARGSORT_DEFAULTS["kind"] = None
+ARGSORT_DEFAULTS["stable"] = None
 
 
 validate_argsort = CompatValidator(
@@ -149,6 +150,7 @@ validate_argsort = CompatValidator(
 ARGSORT_DEFAULTS_KIND: dict[str, int | None] = {}
 ARGSORT_DEFAULTS_KIND["axis"] = -1
 ARGSORT_DEFAULTS_KIND["order"] = None
+ARGSORT_DEFAULTS_KIND["stable"] = None
 validate_argsort_kind = CompatValidator(
     ARGSORT_DEFAULTS_KIND, fname="argsort", max_fname_arg_count=0, method="both"
 )
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
index f8728c61e4..55693d4cdb 100644
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -12126,19 +12126,20 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
             if limit is lib.no_default:
                 cols = self.items() if self.ndim == 2 else [(None, self)]
                 for _, col in cols:
-                    mask = col.isna().values
-                    mask = mask[np.argmax(~mask) :]
-                    if mask.any():
-                        warnings.warn(
-                            "The default fill_method='pad' in "
-                            f"{type(self).__name__}.pct_change is deprecated and will "
-                            "be removed in a future version. Either fill in any "
-                            "non-leading NA values prior to calling pct_change or "
-                            "specify 'fill_method=None' to not fill NA values.",
-                            FutureWarning,
-                            stacklevel=find_stack_level(),
-                        )
-                        break
+                    if len(col) > 0:
+                        mask = col.isna().values
+                        mask = mask[np.argmax(~mask) :]
+                        if mask.any():
+                            warnings.warn(
+                                "The default fill_method='pad' in "
+                                f"{type(self).__name__}.pct_change is deprecated and "
+                                "will be removed in a future version. Either fill in "
+                                "any non-leading NA values prior to calling pct_change "
+                                "or specify 'fill_method=None' to not fill NA values.",
+                                FutureWarning,
+                                stacklevel=find_stack_level(),
+                            )
+                            break
             fill_method = "pad"
         if limit is lib.no_default:
             limit = None
diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py
index 88a08dd55f..c36dcda6e2 100644
--- a/pandas/core/indexes/base.py
+++ b/pandas/core/indexes/base.py
@@ -956,7 +956,7 @@ class Index(IndexOpsMixin, PandasObject):
         return self.__array_wrap__(result)
 
     @final
-    def __array_wrap__(self, result, context=None):
+    def __array_wrap__(self, result, context=None, return_scalar=False):
         """
         Gets called after a ufunc and other functions e.g. np.split.
         """
diff --git a/pandas/core/series.py b/pandas/core/series.py
index 83eb545b9b..f06a1eb533 100644
--- a/pandas/core/series.py
+++ b/pandas/core/series.py
@@ -4066,6 +4066,7 @@ class Series(base.IndexOpsMixin, NDFrame):  # type: ignore[misc]
         axis: Axis = 0,
         kind: SortKind = "quicksort",
         order: None = None,
+        stable: None = None,
     ) -> Series:
         """
         Return the integer indices that would sort the Series values.
@@ -4082,6 +4083,8 @@ class Series(base.IndexOpsMixin, NDFrame):  # type: ignore[misc]
             information. 'mergesort' and 'stable' are the only stable algorithms.
         order : None
             Has no effect but is accepted for compatibility with numpy.
+        stable : None
+            Has no effect but is accepted for compatibility with numpy.
 
         Returns
         -------


Test patch:
diff --git a/pandas/tests/series/methods/test_pct_change.py b/pandas/tests/series/methods/test_pct_change.py
index 9727ef3d5c..6c80e711c3 100644
--- a/pandas/tests/series/methods/test_pct_change.py
+++ b/pandas/tests/series/methods/test_pct_change.py
@@ -118,3 +118,11 @@ def test_pct_change_no_warning_na_beginning():
     result = ser.pct_change()
     expected = Series([np.nan, np.nan, np.nan, 1, 0.5])
     tm.assert_series_equal(result, expected)
+
+
+def test_pct_change_empty():
+    # GH 57056
+    ser = Series([], dtype="float64")
+    expected = ser.copy()
+    result = ser.pct_change(periods=0)
+    tm.assert_series_equal(expected, result)