pola-rs/polars · error · ValueError

parameters `com`, `span`, `half_life`, and `alpha` are mutua

Error message

parameters `com`, `span`, `half_life`, and `alpha` are mutually exclusive

What it means

_prepare_alpha normalises the exponentially-weighted decay specification used by Expr.ewm_mean (and related EWM functions). The four ways to specify decay — com, span, half_life, alpha — are mutually exclusive because each alone determines the smoothing factor; supplying more than one is contradictory and raises ValueError before any computation starts.

Source

Thrown at py-polars/src/polars/expr/expr.py:12793

    def _skip_batch_predicate(self, schema: SchemaDict) -> Expr | None:
        result = self._pyexpr.skip_batch_predicate(schema)
        if result is None:
            return None
        return wrap_expr(result)


def _prepare_alpha(
    com: float | int | None = None,
    span: float | int | None = None,
    half_life: float | int | None = None,
    alpha: float | int | None = None,
) -> float:
    """Normalise EWM decay specification in terms of smoothing factor 'alpha'."""
    if sum((param is not None) for param in (com, span, half_life, alpha)) > 1:
        msg = (
            "parameters `com`, `span`, `half_life`, and `alpha` are mutually exclusive"
        )
        raise ValueError(msg)
    if com is not None:
        if com < 0.0:
            msg = f"require `com` >= 0 (found {com!r})"
            raise ValueError(msg)
        alpha = 1.0 / (1.0 + com)

    elif span is not None:
        if span < 1.0:
            msg = f"require `span` >= 1 (found {span!r})"
            raise ValueError(msg)
        alpha = 2.0 / (span + 1.0)

    elif half_life is not None:
        if half_life <= 0.0:
            msg = f"require `half_life` > 0 (found {half_life!r})"
            raise ValueError(msg)
        alpha = 1.0 - math.exp(-math.log(2.0) / half_life)

View on GitHub (pinned to df599052da)

Solutions

  1. Keep exactly one decay parameter: com, span, half_life, or alpha.
  2. If migrating between specifications, convert the value once (e.g. span=10 implies alpha=2/11) and pass only alpha.
  3. Audit your config dict for EWM keys and strip all but one before calling.

Example fix

# before
pl.col('x').ewm_mean(com=0.5, span=10)

# after
pl.col('x').ewm_mean(span=10)
Defensive patterns

Strategy: validation

Validate before calling

params = {'com': com, 'span': span, 'half_life': half_life, 'alpha': alpha}
provided = [k for k, v in params.items() if v is not None]
assert len(provided) == 1, f'exactly one decay param required, got {provided}'

Prevention

When it happens

Trigger: pl.col('x').ewm_mean(com=0.5, span=10), ewm_mean(span=10, alpha=0.3), or any call where two of the four parameters are non-None.

Common situations: Copy-pasting an ewm_mean example that uses span and merging it with code that already sets com or alpha; config objects exposing all EWM knobs with several filled in; incremental edits that add a 'better' decay parameter without removing the old one.

Related errors


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