{"record":{"id":"74834632fb67de94","repo":"pola-rs/polars","slug":"cannot-call-map-groups-when-filtering-groups-wit","errorCode":null,"errorMessage":"cannot call `map_groups` when filtering groups with `having`","messagePattern":"cannot call `map_groups` when filtering groups with `having`","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/dataframe/group_by.py","lineNumber":447,"sourceCode":"        │ id  ┆ color ┆ shape    │\n        │ --- ┆ ---   ┆ ---      │\n        │ i64 ┆ str   ┆ str      │\n        ╞═════╪═══════╪══════════╡\n        │ 1   ┆ green ┆ triangle │\n        │ 2   ┆ green ┆ square   │\n        │ 4   ┆ red   ┆ square   │\n        │ 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        ----------","sourceCodeStart":429,"sourceCodeEnd":465,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/dataframe/group_by.py#L429-L465","documentation":"GroupBy.map_groups applies an arbitrary Python function to each group's sub-DataFrame via the Rust group_by_map_groups path. When the GroupBy object carries `having` predicates (set by the GroupBy.having() post-aggregation filter API), map_groups cannot honor them — its execution path has no notion of the filter — so polars raises TypeError rather than silently ignoring the filter.","triggerScenarios":"df.group_by('a').having(pl.col('b').sum() > 10).map_groups(fn); chaining .having(...) onto an existing group_by pipeline that ends in map_groups; the same pattern on RollingGroupBy/DynamicGroupBy map_groups variants.","commonSituations":"Porting SQL HAVING clauses onto polars using the fluent having() API; adding group filters to legacy map_groups code during a polars upgrade that introduced having(); mixed declarative/imperative group pipelines.","solutions":["Keep having() with expression aggregations: df.group_by('a').agg(pl.col('b').sum()).having(pl.col('b') > 10) — do not use map_groups on the filtered GroupBy","If map_groups is required, drop having() and filter inside the function (it receives each group frame) or filter the map_groups result afterwards","Rewrite as window expressions with over(...) and filter the frame, as the map_groups docstring recommends"],"exampleFix":"# before\nout = df.group_by('color').having(pl.len() > 2).map_groups(lambda g: g.sorted('size').head(1))\n\n# after\nout = (\n    df.group_by('color')\n      .map_groups(lambda g: g.sorted('size').head(1))\n)\nout = out.filter(pl.count().over('color') > 2)","handlingStrategy":"validation","validationCode":"def safe_map_groups(gb, fn):\n    if getattr(gb, 'predicates', None):\n        raise TypeError('do not combine having() with map_groups; filter after aggregation instead')\n    return gb.map_groups(fn)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep SQL HAVING semantics in the declarative API: group_by().agg() plus having()/filter","Treat having() and map_groups as mutually exclusive when writing group pipelines","For group filtering with custom functions, filter inside fn or filter the map_groups output"],"tags":["polars","group-by","map-groups","having","typeerror","api-combination"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}