Skip to content

Commit 02d4bf3

Browse files
committed
review comments
1 parent 173b7fb commit 02d4bf3

File tree

2 files changed

+45
-29
lines changed

2 files changed

+45
-29
lines changed

pandas/core/apply.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1745,7 +1745,13 @@ def reconstruct_func(
17451745
>>> reconstruct_func("min")
17461746
(False, 'min', None, None)
17471747
"""
1748-
relabeling = func is None and is_multi_agg_with_relabel(**kwargs)
1748+
from pandas.core.groupby.generic import NamedAgg
1749+
1750+
relabeling = func is None and (
1751+
is_multi_agg_with_relabel(**kwargs)
1752+
or any(isinstance(v, NamedAgg) for v in kwargs.values())
1753+
)
1754+
17491755
columns: tuple[str, ...] | None = None
17501756
order: npt.NDArray[np.intp] | None = None
17511757

@@ -1766,9 +1772,20 @@ def reconstruct_func(
17661772
# "Callable[..., Any] | str | list[Callable[..., Any] | str] |
17671773
# MutableMapping[Hashable, Callable[..., Any] | str | list[Callable[..., Any] |
17681774
# str]] | None")
1769-
func, columns, order = normalize_keyword_aggregation( # type: ignore[assignment]
1770-
kwargs
1771-
)
1775+
converted_kwargs = {}
1776+
for key, val in kwargs.items():
1777+
if isinstance(val, NamedAgg):
1778+
aggfunc = val.aggfunc
1779+
if getattr(val, "args", ()) or getattr(val, "kwargs", {}):
1780+
a = getattr(val, "args", ())
1781+
kw = getattr(val, "kwargs", {})
1782+
aggfunc = lambda x, func=aggfunc, a=a, kw=kw: func(x, *a, **kw)
1783+
converted_kwargs[key] = (val.column, aggfunc)
1784+
else:
1785+
converted_kwargs[key] = val
1786+
1787+
func, columns, order = normalize_keyword_aggregation(converted_kwargs)
1788+
17721789
assert func is not None
17731790

17741791
return relabeling, func, columns, order

pandas/core/groupby/generic.py

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010

1111
from collections import abc
1212
from collections.abc import Callable
13+
import dataclasses
1314
from functools import partial
1415
from textwrap import dedent
1516
from typing import (
1617
TYPE_CHECKING,
1718
Any,
1819
Literal,
19-
Self,
2020
TypeAlias,
2121
TypeVar,
2222
cast,
@@ -113,12 +113,11 @@
113113

114114

115115
@set_module("pandas")
116-
class NamedAgg(tuple):
116+
@dataclasses.dataclass
117+
class NamedAgg:
117118
"""
118119
Helper for column specific aggregation with control over output column names.
119120
120-
Subclass of tuple.
121-
122121
Parameters
123122
----------
124123
column : Hashable
@@ -164,32 +163,32 @@ class NamedAgg(tuple):
164163

165164
column: Hashable
166165
aggfunc: AggScalar
166+
args: tuple[Any, ...] = dataclasses.field(default_factory=tuple)
167+
kwargs: dict[str, Any] = dataclasses.field(default_factory=dict)
167168

168-
__slots__ = ()
169-
170-
def __new__(
171-
cls,
169+
def __init__(
170+
self,
172171
column: Hashable,
173172
aggfunc: Callable[..., Any] | str,
174173
*args: Any,
175174
**kwargs: Any,
176-
) -> Self:
177-
if (
178-
callable(aggfunc)
179-
and not getattr(aggfunc, "_is_wrapped", False)
180-
and (args or kwargs)
181-
):
182-
original_func = aggfunc
183-
184-
def wrapped(*call_args: Any, **call_kwargs: Any) -> Any:
185-
series = call_args[0]
186-
final_args = call_args[1:] + args
187-
final_kwargs = {**kwargs, **call_kwargs}
188-
return original_func(series, *final_args, **final_kwargs)
189-
190-
wrapped._is_wrapped = True # type: ignore[attr-defined]
191-
aggfunc = wrapped
192-
return super().__new__(cls, (column, aggfunc))
175+
) -> None:
176+
self.column = column
177+
self.aggfunc = aggfunc
178+
self.args = args
179+
self.kwargs = kwargs
180+
181+
def __getitem__(self, key: int) -> Any:
182+
"""Provide backward-compatible tuple-style access."""
183+
if key == 0:
184+
return self.column
185+
elif key == 1:
186+
return self.aggfunc
187+
elif key == 2:
188+
return self.args
189+
elif key == 3:
190+
return self.kwargs
191+
raise IndexError("index out of range")
193192

194193

195194
@set_module("pandas.api.typing")

0 commit comments

Comments
 (0)