pola-rs/polars · error

cannot call `map_groups` when filtering groups with `having`

Error message

cannot call `map_groups` when filtering groups with `having`

What it means

`df.group_by(...).map_groups(...)` applies a Python function per group. Polars rejects this when the GroupBy also carries `having` predicates (filtering groups), because group-level filtering combined with arbitrary map_groups output has no well-defined result.

Solutions

  1. Apply the group filter before/without having: compute groups with map_groups first, then `filter` the resulting DataFrame.
  2. Use `filter` over a window function instead of `having`: `df.filter(cond.over(group_cols))` then group_by/map_groups.
  3. Split into two steps: aggregate with having, then map_groups on the filtered frame.
  4. Restructure the function so filtering happens inside the per-group function.

Example fix

// before
df.group_by('g').having(pl.len() > 2).map_groups(fn)
// after
df.group_by('g').map_groups(fn).filter(pl.len() > 2)  # or filter groups pre-hoc
Defensive patterns

Strategy: try-catch

Try / catch

try:
    out = gb.map_groups(fn)
except TypeError as e:
    if 'having' in str(e):
        out = df.filter(cond.over(group_cols)).group_by(group_cols).map_groups(fn)

Prevention

When it happens

Trigger: Calling `.group_by(cols).having(pl.col('x') > 1).map_groups(fn)` — i.e. any map_groups after a `having` filter.

Common situations: Building a query fluently where having-filtering was added for an aggregate step and then reused for map_groups; porting SQL HAVING logic onto a map_groups pipeline.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of pola-rs/polars@fe841f959e (2026-09-18). Data as JSON: /api/errors/74834632fb67de94. Report an issue: GitHub.

Appendix: source

Thrown at py-polars/src/polars/dataframe/group_by.py:441

        │ id  ┆ color ┆ shape    │
        │ --- ┆ ---   ┆ ---      │
        │ i64 ┆ str   ┆ str      │
        ╞═════╪═══════╪══════════╡
        │ 1   ┆ green ┆ triangle │
        │ 2   ┆ green ┆ square   │
        │ 4   ┆ red   ┆ square   │
        │ 3   ┆ red   ┆ triangle │
        └─────┴───────┴──────────┘

        It is better to implement this with an expression:

        >>> df.filter(
        ...     pl.int_range(pl.len()).shuffle().over("color") < 2
        ... )  # doctest: +IGNORE_RESULT
        """
        if self.predicates:
            msg = "cannot call `map_groups` when filtering groups with `having`"
            raise TypeError(msg)
        if self.named_by:
            msg = "cannot call `map_groups` when grouping by named expressions"
            raise TypeError(msg)
        by = list(_parse_inputs_as_iterable(self.by))
        if not all(isinstance(c, str) for c in by):
            msg = "cannot call `map_groups` when grouping by an expression"
            raise TypeError(msg)

        return self.df.__class__._from_pydf(
            self.df._df.group_by_map_groups(by, function, self.maintain_order)
        )

    def head(self, n: int = 5) -> DataFrame:
        """
        Get the first `n` rows of each group.

        Parameters
        ----------

View on GitHub (pinned to fe841f959e)