diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cff9c16f5d17a..4a7d6af2fb472 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -135,7 +135,7 @@ repos: files: ^pandas/tests/ - id: FrameOrSeriesUnion name: Check for use of Union[Series, DataFrame] instead of FrameOrSeriesUnion alias - entry: Union\[.*(Series.*DataFrame|DataFrame.*Series).*\] + entry: Union\[.*(Series,.*DataFrame|DataFrame,.*Series).*\] language: pygrep types: [python] exclude: ^pandas/_typing\.py$ diff --git a/pandas/core/apply.py b/pandas/core/apply.py index 0246971bfa2b4..3374e07c53cad 100644 --- a/pandas/core/apply.py +++ b/pandas/core/apply.py @@ -2,7 +2,18 @@ import abc import inspect -from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Optional, Tuple, Type, cast +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Iterator, + List, + Optional, + Tuple, + Type, + Union, + cast, +) import numpy as np @@ -13,6 +24,7 @@ AggFuncType, AggFuncTypeBase, AggFuncTypeDict, + AggObjType, Axis, FrameOrSeriesUnion, ) @@ -34,6 +46,7 @@ if TYPE_CHECKING: from pandas import DataFrame, Index, Series + from pandas.core.groupby import DataFrameGroupBy, SeriesGroupBy ResType = Dict[int, Any] @@ -86,7 +99,7 @@ class Apply(metaclass=abc.ABCMeta): def __init__( self, - obj: FrameOrSeriesUnion, + obj: AggObjType, func, raw: bool, result_type: Optional[str], @@ -646,3 +659,28 @@ def apply_standard(self) -> FrameOrSeriesUnion: return obj._constructor(mapped, index=obj.index).__finalize__( obj, method="apply" ) + + +class GroupByApply(Apply): + obj: Union[SeriesGroupBy, DataFrameGroupBy] + + def __init__( + self, + obj: Union[SeriesGroupBy, DataFrameGroupBy], + func: AggFuncType, + args, + kwds, + ): + kwds = kwds.copy() + self.axis = obj.obj._get_axis_number(kwds.get("axis", 0)) + super().__init__( + obj, + func, + raw=False, + result_type=None, + args=args, + kwds=kwds, + ) + + def apply(self): + raise NotImplementedError diff --git a/pandas/core/groupby/generic.py b/pandas/core/groupby/generic.py index 2bcd5964d3736..92e337caecea3 100644 --- a/pandas/core/groupby/generic.py +++ b/pandas/core/groupby/generic.py @@ -57,11 +57,11 @@ from pandas.core import algorithms, nanops from pandas.core.aggregation import ( agg_list_like, - aggregate, maybe_mangle_lambdas, reconstruct_func, validate_func_kwargs, ) +from pandas.core.apply import GroupByApply from pandas.core.arrays import Categorical, ExtensionArray from pandas.core.base import DataError, SpecificationError import pandas.core.common as com @@ -952,7 +952,8 @@ def aggregate(self, func=None, *args, engine=None, engine_kwargs=None, **kwargs) relabeling, func, columns, order = reconstruct_func(func, **kwargs) func = maybe_mangle_lambdas(func) - result, how = aggregate(self, func, *args, **kwargs) + op = GroupByApply(self, func, args, kwargs) + result, how = op.agg() if how is None: return result diff --git a/pandas/tests/groupby/aggregate/test_aggregate.py b/pandas/tests/groupby/aggregate/test_aggregate.py index 324745fe0eab2..de4ef0996ad49 100644 --- a/pandas/tests/groupby/aggregate/test_aggregate.py +++ b/pandas/tests/groupby/aggregate/test_aggregate.py @@ -203,6 +203,18 @@ def test_aggregate_str_func(tsframe, groupbyfunc): tm.assert_frame_equal(result, expected) +def test_agg_str_with_kwarg_axis_1_raises(df, reduction_func): + gb = df.groupby(level=0) + if reduction_func in ("idxmax", "idxmin"): + error = TypeError + msg = "reduction operation '.*' not allowed for this dtype" + else: + error = ValueError + msg = f"Operation {reduction_func} does not support axis=1" + with pytest.raises(error, match=msg): + gb.agg(reduction_func, axis=1) + + def test_aggregate_item_by_item(df): grouped = df.groupby("A")