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

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.

Source

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

        │ 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 df599052da)

Solutions

  1. 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
  2. If map_groups is required, drop having() and filter inside the function (it receives each group frame) or filter the map_groups result afterwards
  3. Rewrite as window expressions with over(...) and filter the frame, as the map_groups docstring recommends

Example fix

# before
out = df.group_by('color').having(pl.len() > 2).map_groups(lambda g: g.sorted('size').head(1))

# after
out = (
    df.group_by('color')
      .map_groups(lambda g: g.sorted('size').head(1))
)
out = out.filter(pl.count().over('color') > 2)
Defensive patterns

Strategy: validation

Validate before calling

def safe_map_groups(gb, fn):
    if getattr(gb, 'predicates', None):
        raise TypeError('do not combine having() with map_groups; filter after aggregation instead')
    return gb.map_groups(fn)

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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