pandas-dev/pandas · error · ValueError

cannot combine transform and aggregation operations

Error message

cannot combine transform and aggregation operations

What it means

Raised inside `wrap_results_list_like` when concat of per-function results fails with TypeError and the resulting fallback Series is a 'nested object' — meaning some functions returned scalar aggregations while others returned NDFrame-shaped transforms. Mixing these two operation kinds in a single list-like call is structurally ambiguous.

Source

Thrown at pandas/core/apply.py:540

            keys = selected_obj.columns.take(indices)  # type: ignore[assignment]

        return keys, results

    def wrap_results_list_like(
        self, keys: Iterable[Hashable], results: list[Series | DataFrame]
    ):
        obj = self.obj

        try:
            return concat(results, keys=keys, axis=1, sort=False)
        except TypeError as err:
            # we are concatting non-NDFrame objects,
            # e.g. a list of scalars
            from pandas import Series

            result = Series(results, index=keys, name=obj.name)
            if is_nested_object(result):
                raise ValueError(
                    "cannot combine transform and aggregation operations"
                ) from err
            return result

    def agg_dict_like(self) -> DataFrame | Series:
        """
        Compute aggregation in the case of a dict-like argument.

        Returns
        -------
        Result of aggregation.
        """
        return self.agg_or_apply_dict_like(op_name="agg")

    def compute_dict_like(
        self,
        op_name: Literal["agg", "apply"],
        selected_obj: Series | DataFrame,

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Separate the call into an aggregation pass and a transform pass: `df.agg(['sum', 'mean'])` then `df.transform([elemwise_fn])`.
  2. Replace the offending function with one that consistently returns either scalars (for agg) or same-shaped output (for transform).
  3. Use a dict form with explicit column→function mapping to make intent unambiguous.

Example fix

# before
df.agg(['sum', lambda s: s + 1])
# after
agg_part = df.agg(['sum'])
trans_part = df.transform(lambda s: s + 1)
Defensive patterns

Strategy: validation

Validate before calling

import pandas as pd

def classify(func, s):
    out = func(s)
    return 'transform' if isinstance(out, pd.Series) and out.index.equals(s.index) else 'agg'

def split_funcs(funcs, s):
    agg, trans = [], []
    for f in funcs:
        (trans if classify(f, s) == 'transform' else agg).append(f)
    return agg, trans

Type guard

def is_homogeneous_return(funcs, s) -> bool:
    kinds = {classify(f, s) for f in funcs}
    return len(kinds) == 1

Try / catch

try:
    df.agg(funcs)
except ValueError as e:
    if 'cannot combine transform and aggregation' in str(e):
        # split into agg and transform passes
        ...
    raise

Prevention

When it happens

Trigger: `df.agg(['sum', lambda s: s])` — `sum` aggregates to a scalar, the lambda returns same-shape Series (transform). The list mixes reduce and broadcast semantics, so pandas cannot decide the output shape.

Common situations: Combining named aggregations with custom element-wise functions in one list; copy-pasting a mixed function list from a tutorial; refactoring where a transform function slipped into an agg list.

Related errors


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/95ecfc95abe9b387. Report an issue: GitHub.