Skip to content

Commit e39d8a4

Browse files
committed
Implementation
1 parent ec778ce commit e39d8a4

File tree

1 file changed

+115
-3
lines changed

1 file changed

+115
-3
lines changed

pandas/core/groupby/generic.py

+115-3
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
all_indexes_same,
7676
default_index,
7777
)
78+
from pandas.core.reshape.concat import concat
7879
from pandas.core.series import Series
7980
from pandas.core.sorting import get_group_index
8081
from pandas.core.util.numba_ import maybe_use_numba
@@ -1863,15 +1864,126 @@ def _transform_general(self, func, engine, engine_kwargs, *args, **kwargs):
18631864
3 5 9
18641865
4 5 8
18651866
5 5 9
1867+
1868+
Using list-like arguments
1869+
1870+
>>> df = pd.DataFrame({"col": list("aab"), "val": range(3)})
1871+
>>> df.groupby("col").transform(["sum", "min"])
1872+
val
1873+
sum min
1874+
0 1 0
1875+
1 1 0
1876+
2 2 2
1877+
1878+
.. versionchanged:: 3.0.0
1879+
1880+
Named aggregation
1881+
1882+
>>> df = pd.DataFrame({"A": list("aaabbbccc"), "B": range(9), "D": range(9, 18)})
1883+
>>> df.groupby("A").transform(
1884+
... b_min=pd.NamedAgg(column="B", aggfunc="min"),
1885+
... c_sum=pd.NamedAgg(column="D", aggfunc="sum")
1886+
... )
1887+
b_min c_sum
1888+
0 0 30
1889+
1 0 30
1890+
2 0 30
1891+
3 3 39
1892+
4 3 39
1893+
5 3 39
1894+
6 6 48
1895+
7 6 48
1896+
8 6 48
1897+
1898+
.. versionchanged:: 3.0.0
18661899
"""
18671900
)
18681901

18691902
@Substitution(klass="DataFrame", example=__examples_dataframe_doc)
18701903
@Appender(_transform_template)
1871-
def transform(self, func, *args, engine=None, engine_kwargs=None, **kwargs):
1872-
return self._transform(
1873-
func, *args, engine=engine, engine_kwargs=engine_kwargs, **kwargs
1904+
def transform(
1905+
self,
1906+
func: None
1907+
| (Callable | str | list[Callable | str] | dict[str, NamedAgg]) = None,
1908+
*args,
1909+
engine: str | None = None,
1910+
engine_kwargs: dict | None = None,
1911+
**kwargs,
1912+
) -> DataFrame:
1913+
if func is None:
1914+
transformed_func = dict(kwargs.items())
1915+
return self._transform_multiple_funcs(
1916+
transformed_func, *args, engine=engine, engine_kwargs=engine_kwargs
1917+
)
1918+
else:
1919+
if isinstance(func, list):
1920+
func = maybe_mangle_lambdas(func)
1921+
return self._transform_multiple_funcs(
1922+
func, *args, engine=engine, engine_kwargs=engine_kwargs, **kwargs
1923+
)
1924+
else:
1925+
return self._transform(
1926+
func, *args, engine=engine, engine_kwargs=engine_kwargs, **kwargs
1927+
)
1928+
1929+
def _transform_multiple_funcs(
1930+
self,
1931+
func: Any,
1932+
*args,
1933+
engine: str | None = None,
1934+
engine_kwargs: dict | None = None,
1935+
**kwargs,
1936+
) -> DataFrame:
1937+
results = []
1938+
if isinstance(func, dict):
1939+
for name, named_agg in func.items():
1940+
column_name = named_agg.column
1941+
agg_func = named_agg.aggfunc
1942+
result = self._transform_single_column(
1943+
column_name,
1944+
agg_func,
1945+
*args,
1946+
engine=engine,
1947+
engine_kwargs=engine_kwargs,
1948+
**kwargs,
1949+
)
1950+
result.name = name
1951+
results.append(result)
1952+
output = concat(results, axis=1)
1953+
elif isinstance(func, list):
1954+
col_names = []
1955+
columns = [com.get_callable_name(f) or f for f in func]
1956+
func_pairs = zip(columns, func)
1957+
for name, func_item in func_pairs:
1958+
result = self._transform(
1959+
func_item,
1960+
*args,
1961+
engine=engine,
1962+
engine_kwargs=engine_kwargs,
1963+
**kwargs,
1964+
)
1965+
results.append(result)
1966+
col_names.extend([(col, name) for col in result.columns])
1967+
output = concat(results, ignore_index=True, axis=1)
1968+
arrays = [list(x) for x in zip(*col_names)]
1969+
output.columns = MultiIndex.from_arrays(arrays)
1970+
1971+
return output
1972+
1973+
def _transform_single_column(
1974+
self,
1975+
column_name: Hashable,
1976+
agg_func: Callable | str,
1977+
*args,
1978+
engine: str | None = None,
1979+
engine_kwargs: dict | None = None,
1980+
**kwargs,
1981+
) -> Series:
1982+
data = self._gotitem(column_name, ndim=1)
1983+
result = data.transform(
1984+
agg_func, *args, engine=engine, engine_kwargs=engine_kwargs, **kwargs
18741985
)
1986+
return result
18751987

18761988
def _define_paths(self, func, *args, **kwargs):
18771989
if isinstance(func, str):

0 commit comments

Comments
 (0)