{"record":{"id":"e4c30c6e727f05d0","repo":"pola-rs/polars","slug":"cannot-call-map-groups-when-grouping-by-an-expre","errorCode":null,"errorMessage":"cannot call `map_groups` when grouping by an expression","messagePattern":"cannot call `map_groups` when grouping by an expression","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/dataframe/group_by.py","lineNumber":448,"sourceCode":"        │ 3   ┆ red   ┆ triangle │\n        └─────┴───────┴──────────┘\n\n        It is better to implement this with an expression:\n\n        >>> df.filter(\n        ...     pl.int_range(pl.len()).shuffle().over(\"color\") < 2\n        ... )  # doctest: +IGNORE_RESULT\n        \"\"\"\n        if self.predicates:\n            msg = \"cannot call `map_groups` when filtering groups with `having`\"\n            raise TypeError(msg)\n        if self.named_by:\n            msg = \"cannot call `map_groups` when grouping by named expressions\"\n            raise TypeError(msg)\n        by = list(_parse_inputs_as_iterable(self.by))\n        if not all(isinstance(c, str) for c in by):\n            msg = \"cannot call `map_groups` when grouping by an expression\"\n            raise TypeError(msg)\n\n        return self.df.__class__._from_pydf(\n            self.df._df.group_by_map_groups(by, function, self.maintain_order)\n        )\n\n    def head(self, n: int = 5) -> DataFrame:\n        \"\"\"\n        Get the first `n` rows of each group.\n\n        Parameters\n        ----------\n        n\n            Number of rows to return.\n\n        Examples\n        --------\n        >>> df = pl.DataFrame(\n        ...     {","sourceCodeStart":430,"sourceCodeEnd":466,"githubUrl":"https://github.com/pola-rs/polars/blob/fe841f959ef4d2ceefc05a310d33ed7b1ab24e5e/py-polars/src/polars/dataframe/group_by.py#L430-L466","documentation":"`map_groups` passes group keys directly to the Rust `group_by_map_groups`; only column-name strings are supported. This TypeError fires when any group key is a non-string expression (e.g. `pl.col('a')`) rather than a literal column name.","triggerScenarios":"`df.group_by(pl.col('a')).map_groups(fn)` or grouping by a tuple/list containing expressions.","commonSituations":"Reusing an expression-based group_by written for aggregations; dynamic group keys built programmatically as expressions.","solutions":["Replace expressions with the column name string: `group_by('a').map_groups(fn)`.","For computed keys, materialize them with `with_columns` first, then group by the new column name.","Use `.over(key_expr)` expression APIs if a plain transformation (not arbitrary Python fn) is needed."],"exampleFix":"// before\ndf.group_by(pl.col('region')).map_groups(fn)\n// after\ndf.group_by('region').map_groups(fn)","handlingStrategy":"type-guard","validationCode":"import polars as pl\nkeys = ['region']\nassert all(isinstance(k, str) for k in keys), 'group keys for map_groups must be strings'","typeGuard":"def are_column_names(keys) -> bool:\n    return all(isinstance(k, str) for k in keys)","tryCatchPattern":"try:\n    out = gb.map_groups(fn)\nexcept TypeError:\n    names = [e.meta.output_name() for e in key_exprs]\n    out = df.group_by(names).map_groups(fn)","preventionTips":["Always pass column-name strings to group_by before map_groups","Convert pl.col('x') to 'x' at call sites","Use .over() for expression-based grouping needs"],"tags":["python","polars","group-by","type-error"],"backgroundTag":"unsupported-operation","analyzedSha":"fe841f959ef4d2ceefc05a310d33ed7b1ab24e5e","analyzedAt":"2026-09-18T22:14:11.667Z","contentChangedAt":"2026-09-18T22:14:11.667Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}