Skip to content

Commit 73fe6b8

Browse files
authored
BUG: Fix to GH34422 SeriesGroupBy works only with 'func' now (#34435)
1 parent bfecc48 commit 73fe6b8

File tree

4 files changed

+55
-6
lines changed

4 files changed

+55
-6
lines changed

doc/source/whatsnew/v1.1.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,7 @@ Groupby/resample/rolling
975975
to the input DataFrame is inconsistent. An internal heuristic to detect index mutation would behave differently for equal but not identical
976976
indices. In particular, the result index shape might change if a copy of the input would be returned.
977977
The behaviour now is consistent, independent of internal heuristics. (:issue:`31612`, :issue:`14927`, :issue:`13056`)
978+
- Bug in :meth:`SeriesGroupBy.agg` where any column name was accepted in the named aggregation of ``SeriesGroupBy`` previously. The behaviour now allows only ``str`` and callables else would raise ``TypeError``. (:issue:`34422`)
978979

979980
Reshaping
980981
^^^^^^^^^

pandas/core/aggregation.py

+37-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from collections import defaultdict
77
from functools import partial
8-
from typing import Any, DefaultDict, List, Sequence, Tuple
8+
from typing import Any, Callable, DefaultDict, List, Sequence, Tuple, Union
99

1010
from pandas.core.dtypes.common import is_dict_like, is_list_like
1111

@@ -196,3 +196,39 @@ def maybe_mangle_lambdas(agg_spec: Any) -> Any:
196196
mangled_aggspec = _managle_lambda_list(agg_spec)
197197

198198
return mangled_aggspec
199+
200+
201+
def validate_func_kwargs(
202+
kwargs: dict,
203+
) -> Tuple[List[str], List[Union[str, Callable[..., Any]]]]:
204+
"""
205+
Validates types of user-provided "named aggregation" kwargs.
206+
`TypeError` is raised if aggfunc is not `str` or callable.
207+
208+
Parameters
209+
----------
210+
kwargs : dict
211+
212+
Returns
213+
-------
214+
columns : List[str]
215+
List of user-provied keys.
216+
func : List[Union[str, callable[...,Any]]]
217+
List of user-provided aggfuncs
218+
219+
Examples
220+
--------
221+
>>> validate_func_kwargs({'one': 'min', 'two': 'max'})
222+
(['one', 'two'], ['min', 'max'])
223+
"""
224+
no_arg_message = "Must provide 'func' or named aggregation **kwargs."
225+
tuple_given_message = "func is expected but recieved {} in **kwargs."
226+
columns = list(kwargs)
227+
func = []
228+
for col_func in kwargs.values():
229+
if not (isinstance(col_func, str) or callable(col_func)):
230+
raise TypeError(tuple_given_message.format(type(col_func).__name__))
231+
func.append(col_func)
232+
if not columns:
233+
raise TypeError(no_arg_message)
234+
return columns, func

pandas/core/groupby/generic.py

+2-5
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
is_multi_agg_with_relabel,
5858
maybe_mangle_lambdas,
5959
normalize_keyword_aggregation,
60+
validate_func_kwargs,
6061
)
6162
import pandas.core.algorithms as algorithms
6263
from pandas.core.base import DataError, SpecificationError
@@ -233,13 +234,9 @@ def aggregate(
233234

234235
relabeling = func is None
235236
columns = None
236-
no_arg_message = "Must provide 'func' or named aggregation **kwargs."
237237
if relabeling:
238-
columns = list(kwargs)
239-
func = [kwargs[col] for col in columns]
238+
columns, func = validate_func_kwargs(kwargs)
240239
kwargs = {}
241-
if not columns:
242-
raise TypeError(no_arg_message)
243240

244241
if isinstance(func, str):
245242
return getattr(self, func)(*args, **kwargs)

pandas/tests/groupby/aggregate/test_aggregate.py

+15
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,21 @@ def test_mangled(self):
511511
expected = pd.DataFrame({"a": [0, 0], "b": [1, 1]})
512512
tm.assert_frame_equal(result, expected)
513513

514+
@pytest.mark.parametrize(
515+
"inp",
516+
[
517+
pd.NamedAgg(column="anything", aggfunc="min"),
518+
("anything", "min"),
519+
["anything", "min"],
520+
],
521+
)
522+
def test_named_agg_nametuple(self, inp):
523+
# GH34422
524+
s = pd.Series([1, 1, 2, 2, 3, 3, 4, 5])
525+
msg = f"func is expected but recieved {type(inp).__name__}"
526+
with pytest.raises(TypeError, match=msg):
527+
s.groupby(s.values).agg(a=inp)
528+
514529

515530
class TestNamedAggregationDataFrame:
516531
def test_agg_relabel(self):

0 commit comments

Comments
 (0)