pola-rs/polars · error · ValueError

`percentiles` must all be in the range [0, 1]

Error message

`percentiles` must all be in the range [0, 1]

What it means

ValueError from parse_percentiles (py-polars/src/polars/_utils/various.py:588-603). LazyFrame.describe(percentiles=...) (and any API routing through parse_percentiles) requires every supplied percentile to be a fraction in the closed interval [0.0, 1.0]; polars then internally injects the median (0.5) and sorts the list. Values outside [0, 1] — most commonly whole-number percentages like 50 or 95 — raise immediately.

Source

Thrown at py-polars/src/polars/_utils/various.py:603

    return False


def parse_percentiles(
    percentiles: Sequence[float] | float | None, *, inject_median: bool = False
) -> Sequence[float]:
    """
    Transforms raw percentiles into our preferred format, adding the 50th percentile.

    Raises a ValueError if the percentile sequence is invalid
    (e.g. outside the range [0, 1])
    """
    if isinstance(percentiles, float):
        percentiles = [percentiles]
    elif percentiles is None:
        percentiles = []
    if not all((0 <= p <= 1) for p in percentiles):
        msg = "`percentiles` must all be in the range [0, 1]"
        raise ValueError(msg)

    sub_50_percentiles = sorted(p for p in percentiles if p < 0.5)
    at_or_above_50_percentiles = sorted(p for p in percentiles if p >= 0.5)

    if inject_median and (
        not at_or_above_50_percentiles or at_or_above_50_percentiles[0] != 0.5
    ):
        at_or_above_50_percentiles = [0.5, *at_or_above_50_percentiles]

    return [*sub_50_percentiles, *at_or_above_50_percentiles]


def re_escape(s: str) -> str:
    """Escape a string for use in a Polars (Rust) regex."""
    # note: almost the same as the standard python 're.escape' function, but
    # escapes _only_ those metachars with meaning to the rust regex crate
    re_rust_metachars = r"\\?()|\[\]{}^$#&~.+*-"
    return re.sub(f"([{re_rust_metachars}])", r"\\\1", s)

View on GitHub (pinned to df599052da)

Solutions

  1. Convert percentages to fractions: divide by 100 ([50, 95] -> [0.5, 0.95])
  2. Drop the value entirely if you meant the median — 0.5 is injected automatically
  3. Validate user input before the call: assert all(0 <= p <= 1 for p in percentiles)

Example fix

# before
lf.describe(percentiles=[10, 50, 90])  # ValueError

# after
lf.describe(percentiles=[0.10, 0.50, 0.90])
Defensive patterns

Strategy: validation

Validate before calling

def norm_percentiles(ps: list[float]) -> list[float]:
    out = [p / 100 if p > 1 else p for p in ps]
    if not all(0.0 <= p <= 1.0 for p in out):
        raise ValueError('percentiles must be fractions in [0, 1]')
    return out

Prevention

When it happens

Trigger: lf.describe(percentiles=[0.1, 50, 0.9]); lf.describe(percentiles=95); negative values or values > 1 such as [1.5].

Common situations: Copy-pasting percentile integers from configure-style tooling or report specs ('show the 95th percentile'); mixing units with APIs that take 0-100 (some plotting/reporting libs); user-facing knobs forwarded unvalidated.

Related errors


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