Repository: pandas-dev/pandas
Pull request #58935 (2024-06-05 15:36:50)

DOC: fix PR07 for pandas.pivot
fixes

```
pandas.pivot PR07
```


Patch:
diff --git a/ci/code_checks.sh b/ci/code_checks.sh
index 039700f306..705f01d74a 100755
--- a/ci/code_checks.sh
+++ b/ci/code_checks.sh
@@ -469,12 +469,10 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
         -i "pandas.merge_asof PR07,RT03" \
         -i "pandas.merge_ordered PR07" \
         -i "pandas.period_range RT03,SA01" \
-        -i "pandas.pivot PR07" \
         -i "pandas.plotting.andrews_curves RT03,SA01" \
         -i "pandas.plotting.lag_plot RT03,SA01" \
         -i "pandas.plotting.scatter_matrix PR07,SA01" \
         -i "pandas.qcut PR07,SA01" \
-        -i "pandas.read_spss SA01" \
         -i "pandas.set_eng_float_format RT03,SA01" \
         -i "pandas.testing.assert_extension_array_equal SA01" \
         -i "pandas.tseries.offsets.BDay PR02,SA01" \
diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst
index 7f5c879c0d..802a9fd7e2 100644
--- a/doc/source/whatsnew/v3.0.0.rst
+++ b/doc/source/whatsnew/v3.0.0.rst
@@ -456,6 +456,7 @@ Datetimelike
 - Bug in :func:`date_range` where the last valid timestamp would sometimes not be produced (:issue:`56134`)
 - Bug in :func:`date_range` where using a negative frequency value would not include all points between the start and end values (:issue:`56382`)
 - Bug in :func:`tseries.api.guess_datetime_format` would fail to infer time format when "%Y" == "%H%M" (:issue:`57452`)
+- Bug in :meth:`Dataframe.agg` with df with missing values resulting in IndexError (:issue:`58810`)
 - Bug in :meth:`DatetimeIndex.is_year_start` and :meth:`DatetimeIndex.is_quarter_start` does not raise on Custom business days frequencies bigger then "1C" (:issue:`58664`)
 - Bug in :meth:`DatetimeIndex.is_year_start` and :meth:`DatetimeIndex.is_quarter_start` returning ``False`` on double-digit frequencies (:issue:`58523`)
 - Bug in setting scalar values with mismatched resolution into arrays with non-nanosecond ``datetime64``, ``timedelta64`` or :class:`DatetimeTZDtype` incorrectly truncating those scalars (:issue:`56410`)
diff --git a/pandas/core/apply.py b/pandas/core/apply.py
index 33f5062358..2039386c47 100644
--- a/pandas/core/apply.py
+++ b/pandas/core/apply.py
@@ -1850,11 +1850,13 @@ def relabel_result(
                 com.get_callable_name(f) if not isinstance(f, str) else f for f in fun
             ]
             col_idx_order = Index(s.index).get_indexer(fun)
-            s = s.iloc[col_idx_order]
-
+            valid_idx = col_idx_order != -1
+            if valid_idx.any():
+                s = s.iloc[col_idx_order[valid_idx]]
         # assign the new user-provided "named aggregation" as index names, and reindex
         # it based on the whole user-provided names.
-        s.index = reordered_indexes[idx : idx + len(fun)]
+        if not s.empty:
+            s.index = reordered_indexes[idx : idx + len(fun)]
         reordered_result_in_dict[col] = s.reindex(columns)
         idx = idx + len(fun)
     return reordered_result_in_dict
diff --git a/pandas/core/reshape/pivot.py b/pandas/core/reshape/pivot.py
index 86da19f13b..ff993c039b 100644
--- a/pandas/core/reshape/pivot.py
+++ b/pandas/core/reshape/pivot.py
@@ -11,10 +11,6 @@ from typing import (
 import numpy as np
 
 from pandas._libs import lib
-from pandas.util._decorators import (
-    Appender,
-    Substitution,
-)
 
 from pandas.core.dtypes.cast import maybe_downcast_to_dtype
 from pandas.core.dtypes.common import (
@@ -29,7 +25,6 @@ from pandas.core.dtypes.generic import (
 )
 
 import pandas.core.common as com
-from pandas.core.frame import _shared_docs
 from pandas.core.groupby import Grouper
 from pandas.core.indexes.api import (
     Index,
@@ -656,8 +651,6 @@ def _convert_by(by):
     return by
 
 
-@Substitution("\ndata : DataFrame")
-@Appender(_shared_docs["pivot"], indents=1)
 def pivot(
     data: DataFrame,
     *,
@@ -665,6 +658,152 @@ def pivot(
     index: IndexLabel | lib.NoDefault = lib.no_default,
     values: IndexLabel | lib.NoDefault = lib.no_default,
 ) -> DataFrame:
+    """
+    Return reshaped DataFrame organized by given index / column values.
+
+    Reshape data (produce a "pivot" table) based on column values. Uses
+    unique values from specified `index` / `columns` to form axes of the
+    resulting DataFrame. This function does not support data
+    aggregation, multiple values will result in a MultiIndex in the
+    columns. See the :ref:`User Guide <reshaping>` for more on reshaping.
+
+    Parameters
+    ----------
+    data : DataFrame
+        Input pandas DataFrame object.
+    columns : str or object or a list of str
+        Column to use to make new frame's columns.
+    index : str or object or a list of str, optional
+        Column to use to make new frame's index. If not given, uses existing index.
+    values : str, object or a list of the previous, optional
+        Column(s) to use for populating new frame's values. If not
+        specified, all remaining columns will be used and the result will
+        have hierarchically indexed columns.
+
+    Returns
+    -------
+    DataFrame
+        Returns reshaped DataFrame.
+
+    Raises
+    ------
+    ValueError:
+        When there are any `index`, `columns` combinations with multiple
+        values. `DataFrame.pivot_table` when you need to aggregate.
+
+    See Also
+    --------
+    DataFrame.pivot_table : Generalization of pivot that can handle
+        duplicate values for one index/column pair.
+    DataFrame.unstack : Pivot based on the index values instead of a
+        column.
+    wide_to_long : Wide panel to long format. Less flexible but more
+        user-friendly than melt.
+
+    Notes
+    -----
+    For finer-tuned control, see hierarchical indexing documentation along
+    with the related stack/unstack methods.
+
+    Reference :ref:`the user guide <reshaping.pivot>` for more examples.
+
+    Examples
+    --------
+    >>> df = pd.DataFrame(
+    ...     {
+    ...         "foo": ["one", "one", "one", "two", "two", "two"],
+    ...         "bar": ["A", "B", "C", "A", "B", "C"],
+    ...         "baz": [1, 2, 3, 4, 5, 6],
+    ...         "zoo": ["x", "y", "z", "q", "w", "t"],
+    ...     }
+    ... )
+    >>> df
+        foo   bar  baz  zoo
+    0   one   A    1    x
+    1   one   B    2    y
+    2   one   C    3    z
+    3   two   A    4    q
+    4   two   B    5    w
+    5   two   C    6    t
+
+    >>> df.pivot(index="foo", columns="bar", values="baz")
+    bar  A   B   C
+    foo
+    one  1   2   3
+    two  4   5   6
+
+    >>> df.pivot(index="foo", columns="bar")["baz"]
+    bar  A   B   C
+    foo
+    one  1   2   3
+    two  4   5   6
+
+    >>> df.pivot(index="foo", columns="bar", values=["baz", "zoo"])
+          baz       zoo
+    bar   A  B  C   A  B  C
+    foo
+    one   1  2  3   x  y  z
+    two   4  5  6   q  w  t
+
+    You could also assign a list of column names or a list of index names.
+
+    >>> df = pd.DataFrame(
+    ...     {
+    ...         "lev1": [1, 1, 1, 2, 2, 2],
+    ...         "lev2": [1, 1, 2, 1, 1, 2],
+    ...         "lev3": [1, 2, 1, 2, 1, 2],
+    ...         "lev4": [1, 2, 3, 4, 5, 6],
+    ...         "values": [0, 1, 2, 3, 4, 5],
+    ...     }
+    ... )
+    >>> df
+        lev1 lev2 lev3 lev4 values
+    0   1    1    1    1    0
+    1   1    1    2    2    1
+    2   1    2    1    3    2
+    3   2    1    2    4    3
+    4   2    1    1    5    4
+    5   2    2    2    6    5
+
+    >>> df.pivot(index="lev1", columns=["lev2", "lev3"], values="values")
+    lev2    1         2
+    lev3    1    2    1    2
+    lev1
+    1     0.0  1.0  2.0  NaN
+    2     4.0  3.0  NaN  5.0
+
+    >>> df.pivot(index=["lev1", "lev2"], columns=["lev3"], values="values")
+          lev3    1    2
+    lev1  lev2
+       1     1  0.0  1.0
+             2  2.0  NaN
+       2     1  4.0  3.0
+             2  NaN  5.0
+
+    A ValueError is raised if there are any duplicates.
+
+    >>> df = pd.DataFrame(
+    ...     {
+    ...         "foo": ["one", "one", "two", "two"],
+    ...         "bar": ["A", "A", "B", "C"],
+    ...         "baz": [1, 2, 3, 4],
+    ...     }
+    ... )
+    >>> df
+       foo bar  baz
+    0  one   A    1
+    1  one   A    2
+    2  two   B    3
+    3  two   C    4
+
+    Notice that the first two rows are the same for our `index`
+    and `columns` arguments.
+
+    >>> df.pivot(index="foo", columns="bar", values="baz")
+    Traceback (most recent call last):
+       ...
+    ValueError: Index contains duplicate entries, cannot reshape
+    """
     columns_listlike = com.convert_to_list_like(columns)
 
     # If columns is None we will create a MultiIndex level with None as name
diff --git a/pandas/io/spss.py b/pandas/io/spss.py
index 2c464cc7e9..313ffa79cb 100644
--- a/pandas/io/spss.py
+++ b/pandas/io/spss.py
@@ -52,6 +52,14 @@ def read_spss(
     DataFrame
         DataFrame based on the SPSS file.
 
+    See Also
+    --------
+    read_csv : Read a comma-separated values (csv) file into a pandas DataFrame.
+    read_excel : Read an Excel file into a pandas DataFrame.
+    read_sas : Read an SAS file into a pandas DataFrame.
+    read_orc : Load an ORC object into a pandas DataFrame.
+    read_feather : Load a feather-format object into a pandas DataFrame.
+
     Examples
     --------
     >>> df = pd.read_spss("spss_data.sav")  # doctest: +SKIP


Test patch:
diff --git a/pandas/tests/groupby/aggregate/test_aggregate.py b/pandas/tests/groupby/aggregate/test_aggregate.py
index 1f140063fd..26602baedb 100644
--- a/pandas/tests/groupby/aggregate/test_aggregate.py
+++ b/pandas/tests/groupby/aggregate/test_aggregate.py
@@ -62,6 +62,32 @@ def test_agg_ser_multi_key(df):
     tm.assert_series_equal(results, expected)
 
 
+def test_agg_with_missing_values():
+    # GH#58810
+    missing_df = DataFrame(
+        {
+            "nan": [np.nan, np.nan, np.nan, np.nan],
+            "na": [pd.NA, pd.NA, pd.NA, pd.NA],
+            "nat": [pd.NaT, pd.NaT, pd.NaT, pd.NaT],
+            "none": [None, None, None, None],
+            "values": [1, 2, 3, 4],
+        }
+    )
+
+    result = missing_df.agg(x=("nan", "min"), y=("na", "min"), z=("values", "sum"))
+
+    expected = DataFrame(
+        {
+            "nan": [np.nan, np.nan, np.nan],
+            "na": [np.nan, np.nan, np.nan],
+            "values": [np.nan, np.nan, 10.0],
+        },
+        index=["x", "y", "z"],
+    )
+
+    tm.assert_frame_equal(result, expected)
+
+
 def test_groupby_aggregation_mixed_dtype():
     # GH 6212
     expected = DataFrame(