diff --git a/pandas/core/aggregation.py b/pandas/core/aggregation.py index 541c617f7f618..69ed78921607a 100644 --- a/pandas/core/aggregation.py +++ b/pandas/core/aggregation.py @@ -415,8 +415,6 @@ def transform( ValueError If the transform function fails or does not transform. """ - from pandas.core.reshape.concat import concat - is_series = obj.ndim == 1 if obj._get_axis_number(axis) == 1: @@ -430,42 +428,11 @@ def transform( func = {col: func for col in obj} if isinstance(func, dict): - if not is_series: - cols = sorted(set(func.keys()) - set(obj.columns)) - if len(cols) > 0: - raise SpecificationError(f"Column(s) {cols} do not exist") - - if any(isinstance(v, dict) for v in func.values()): - # GH 15931 - deprecation of renaming keys - raise SpecificationError("nested renamer is not supported") - - results = {} - for name, how in func.items(): - colg = obj._gotitem(name, ndim=1) - try: - results[name] = transform(colg, how, 0, *args, **kwargs) - except Exception as e: - if str(e) == "Function did not transform": - raise e - - # combine results - if len(results) == 0: - raise ValueError("Transform function failed") - return concat(results, axis=1) + return transform_dict_like(obj, func, *args, **kwargs) # func is either str or callable try: - if isinstance(func, str): - result = obj._try_aggregate_string_function(func, *args, **kwargs) - else: - f = obj._get_cython_func(func) - if f and not args and not kwargs: - result = getattr(obj, f)() - else: - try: - result = obj.apply(func, args=args, **kwargs) - except Exception: - result = func(obj, *args, **kwargs) + result = transform_str_or_callable(obj, func, *args, **kwargs) except Exception: raise ValueError("Transform function failed") @@ -479,3 +446,52 @@ def transform( raise ValueError("Function did not transform") return result + + +def transform_dict_like(obj, func, *args, **kwargs): + """ + Compute transform in the case of a dict-like func + """ + from pandas.core.reshape.concat import concat + + if obj.ndim != 1: + cols = sorted(set(func.keys()) - set(obj.columns)) + if len(cols) > 0: + raise SpecificationError(f"Column(s) {cols} do not exist") + + if any(isinstance(v, dict) for v in func.values()): + # GH 15931 - deprecation of renaming keys + raise SpecificationError("nested renamer is not supported") + + results = {} + for name, how in func.items(): + colg = obj._gotitem(name, ndim=1) + try: + results[name] = transform(colg, how, 0, *args, **kwargs) + except Exception as e: + if str(e) == "Function did not transform": + raise e + + # combine results + if len(results) == 0: + raise ValueError("Transform function failed") + return concat(results, axis=1) + + +def transform_str_or_callable(obj, func, *args, **kwargs): + """ + Compute transform in the case of a string or callable func + """ + if isinstance(func, str): + return obj._try_aggregate_string_function(func, *args, **kwargs) + + if not args and not kwargs: + f = obj._get_cython_func(func) + if f: + return getattr(obj, f)() + + # Two possible ways to use a UDF - apply or call directly + try: + return obj.apply(func, args=args, **kwargs) + except Exception: + return func(obj, *args, **kwargs)