Skip to content

Discrete distributions

Every discrete distribution inherits the shared method surface from DiscreteDistribution (shown first); the concrete classes follow, alphabetically. The members listed under each class include those inherited methods.

Base class

DiscreteDistribution

Abstract base class for discrete univariate distributions.

pmf

pmf(value: float | IntoExprColumn) -> Expr

Probability mass function, P(X = value). Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def pmf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Probability mass function, `P(X = value)`. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._pmf(v))

log_pmf

log_pmf(value: float | IntoExprColumn) -> Expr

Natural logarithm of the pmf. Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def log_pmf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Natural logarithm of the pmf. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._log_pmf(v))

sample

sample(seed: int | None = None) -> Expr

Draw one random variate per row.

Returns a column with one variate per input row, in the distribution's element dtype (Float64, UInt64 or Boolean). Output length follows the surrounding context (frame length under select / with_columns, partition length under over / group_by), and each row's draw is derived from a per-row sub-seed mixed from seed and the row's position, so the result is independent of Polars chunking and thread scheduling.

A row with an invalid parameter raises; a row with a null parameter yields null. The output is named "sample" when every parameter is constant (the fast path); with any column-valued parameter the name follows the first parameter expression (polars root-name semantics, so .name.* modifiers keep working).

Source code in polars_stats/distributions/_base.py
def sample(self, seed: int | None = None) -> pl.Expr:
    """Draw one random variate per row.

    Returns a column with one variate per input row, in the distribution's element dtype
    (`Float64`, `UInt64` or `Boolean`). Output length follows the surrounding context (frame length
    under `select` / `with_columns`, partition length under `over` / `group_by`), and each row's draw
    is derived from a per-row sub-seed mixed from `seed` and the row's position, so the result is
    independent of Polars chunking and thread scheduling.

    A row with an invalid parameter raises; a row with a null parameter yields null. The output is
    named `"sample"` when every parameter is constant (the fast path); with any column-valued
    parameter the name follows the first parameter expression (polars root-name semantics, so
    `.name.*` modifiers keep working).
    """
    if self._scalar_kwargs is not None:
        return register_plugin(
            f"{self._plugin_prefix}_sample_scalar",
            (ROW_INDEX_EXPR,),
            kwargs={"seed": seed, **self._scalar_kwargs},
        ).alias("sample")
    return register_plugin(
        f"{self._plugin_prefix}_sample", (*self._param_exprs, ROW_INDEX_EXPR), kwargs={"seed": seed}
    )

samples

samples(size: int, seed: int | None = None) -> Expr

Draw size random variates per row, returning Array(inner=<element dtype>, shape=size).

Each row's size draws are consecutive values from one per-row random stream keyed by seed and the row's position, so the result is reproducible for a fixed seed and independent of Polars chunking and thread scheduling. samples(size=1) matches sample for the same seed, and growing size extends each row's array without changing the existing draws.

A row with a null parameter yields a null array (not an array of null elements), produced natively by the plugin via the output's outer validity; an invalid parameterisation raises.

Naming follows sample: "samples" with all-constant parameters, the first parameter expression's root name otherwise.

Source code in polars_stats/distributions/_base.py
def samples(self, size: int, seed: int | None = None) -> pl.Expr:
    """Draw `size` random variates per row, returning `Array(inner=<element dtype>, shape=size)`.

    Each row's `size` draws are consecutive values from one per-row random stream keyed by `seed` and the
    row's position, so the result is reproducible for a fixed `seed` and independent of Polars chunking and
    thread scheduling. `samples(size=1)` matches `sample` for the same seed, and growing `size` extends each
    row's array without changing the existing draws.

    A row with a null parameter yields a null array (not an array of null elements), produced natively by
    the plugin via the output's outer validity; an invalid parameterisation raises.

    Naming follows `sample`: `"samples"` with all-constant parameters, the first parameter
    expression's root name otherwise.
    """
    if size <= 0:
        msg = f"size must be a positive integer, got {size}"
        raise ValueError(msg)
    out = self._samples(size=size, seed=seed)
    return out.alias("samples") if self._scalar_kwargs is not None else out

cdf

cdf(value: float | IntoExprColumn) -> Expr

Cumulative distribution function, P(X <= value). Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def cdf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Cumulative distribution function, `P(X <= value)`. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._cdf(v))

log_cdf

log_cdf(value: float | IntoExprColumn) -> Expr

Natural logarithm of the cdf. Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def log_cdf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Natural logarithm of the cdf. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._log_cdf(v))

sf

sf(value: float | IntoExprColumn) -> Expr

Survival function, P(X > value) = 1 - cdf(value). Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def sf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Survival function, `P(X > value) = 1 - cdf(value)`. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._sf(v))

log_sf

log_sf(value: float | IntoExprColumn) -> Expr

Natural logarithm of the survival function. Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def log_sf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Natural logarithm of the survival function. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._log_sf(v))

ppf

ppf(quantile: float | IntoExprColumn) -> Expr

Percent point function (inverse cdf).

A quantile outside [0, 1] yields null.

Nulls are propagated and a NaN quantile yields NaN, matching scipy.

Source code in polars_stats/distributions/_base.py
def ppf(self, quantile: float | IntoExprColumn) -> pl.Expr:
    """Percent point function (inverse cdf).

    A `quantile` outside `[0, 1]` yields **null**.

    Nulls are propagated and a `NaN` quantile yields `NaN`, matching scipy.
    """
    q = as_expr(quantile)
    return propagate_null_and_nan(q, self._ppf(q))

isf

isf(quantile: float | IntoExprColumn) -> Expr

Inverse survival function, the value x with sf(x) == quantile.

Same domain contract as ppf, with the endpoints reversed: quantile outside [0, 1] yields null, nulls propagate, NaN yields NaN.

Source code in polars_stats/distributions/_base.py
def isf(self, quantile: float | IntoExprColumn) -> pl.Expr:
    """Inverse survival function, the value `x` with `sf(x) == quantile`.

    Same domain contract as `ppf`, with the endpoints reversed: `quantile` outside `[0, 1]` yields
    null, nulls propagate, `NaN` yields `NaN`.
    """
    q = as_expr(quantile)
    return propagate_null_and_nan(q, self._isf(q))

mean abstractmethod

mean() -> Expr

Expected value E[X].

Source code in polars_stats/distributions/_base.py
@abstractmethod
def mean(self) -> pl.Expr:
    """Expected value `E[X]`."""

variance abstractmethod

variance() -> Expr

Variance Var[X] = E[(X - E[X])^2].

Source code in polars_stats/distributions/_base.py
@abstractmethod
def variance(self) -> pl.Expr:
    """Variance `Var[X] = E[(X - E[X])^2]`."""

std

std() -> Expr

Standard deviation, sqrt(variance).

Source code in polars_stats/distributions/_base.py
def std(self) -> pl.Expr:
    """Standard deviation, `sqrt(variance)`."""
    return self.variance().sqrt()

median

median() -> Expr

Median, ppf(0.5).

Source code in polars_stats/distributions/_base.py
def median(self) -> pl.Expr:
    """Median, `ppf(0.5)`."""
    return self.ppf(0.5)

entropy abstractmethod

entropy() -> Expr

Differential or Shannon entropy, in nats.

Source code in polars_stats/distributions/_base.py
@abstractmethod
def entropy(self) -> pl.Expr:
    """Differential or Shannon entropy, in nats."""

Distributions

Bernoulli

Bernoulli(p: float | IntoExprColumn)

Bases: DiscreteDistribution

Bernoulli distribution with success probability p.

Parameters:

Name Type Description Default
p float | IntoExprColumn

Success probability of Bernoulli distribution. Either a Python float or an IntoExprColumn (pl.Expr, pl.Series or column name str) carrying one probability per row.

required
Source code in polars_stats/distributions/_bernoulli.py
def __init__(self, p: float | IntoExprColumn) -> None:
    self._p = coerce_param(p, name="p")
    self._scalar_kwargs = scalar_kwargs(p=scalar_float(p))

sample

sample(seed: int | None = None) -> Expr

Draw one random variate per row.

Returns a column with one variate per input row, in the distribution's element dtype (Float64, UInt64 or Boolean). Output length follows the surrounding context (frame length under select / with_columns, partition length under over / group_by), and each row's draw is derived from a per-row sub-seed mixed from seed and the row's position, so the result is independent of Polars chunking and thread scheduling.

A row with an invalid parameter raises; a row with a null parameter yields null. The output is named "sample" when every parameter is constant (the fast path); with any column-valued parameter the name follows the first parameter expression (polars root-name semantics, so .name.* modifiers keep working).

Source code in polars_stats/distributions/_base.py
def sample(self, seed: int | None = None) -> pl.Expr:
    """Draw one random variate per row.

    Returns a column with one variate per input row, in the distribution's element dtype
    (`Float64`, `UInt64` or `Boolean`). Output length follows the surrounding context (frame length
    under `select` / `with_columns`, partition length under `over` / `group_by`), and each row's draw
    is derived from a per-row sub-seed mixed from `seed` and the row's position, so the result is
    independent of Polars chunking and thread scheduling.

    A row with an invalid parameter raises; a row with a null parameter yields null. The output is
    named `"sample"` when every parameter is constant (the fast path); with any column-valued
    parameter the name follows the first parameter expression (polars root-name semantics, so
    `.name.*` modifiers keep working).
    """
    if self._scalar_kwargs is not None:
        return register_plugin(
            f"{self._plugin_prefix}_sample_scalar",
            (ROW_INDEX_EXPR,),
            kwargs={"seed": seed, **self._scalar_kwargs},
        ).alias("sample")
    return register_plugin(
        f"{self._plugin_prefix}_sample", (*self._param_exprs, ROW_INDEX_EXPR), kwargs={"seed": seed}
    )

samples

samples(size: int, seed: int | None = None) -> Expr

Draw size random variates per row, returning Array(inner=<element dtype>, shape=size).

Each row's size draws are consecutive values from one per-row random stream keyed by seed and the row's position, so the result is reproducible for a fixed seed and independent of Polars chunking and thread scheduling. samples(size=1) matches sample for the same seed, and growing size extends each row's array without changing the existing draws.

A row with a null parameter yields a null array (not an array of null elements), produced natively by the plugin via the output's outer validity; an invalid parameterisation raises.

Naming follows sample: "samples" with all-constant parameters, the first parameter expression's root name otherwise.

Source code in polars_stats/distributions/_base.py
def samples(self, size: int, seed: int | None = None) -> pl.Expr:
    """Draw `size` random variates per row, returning `Array(inner=<element dtype>, shape=size)`.

    Each row's `size` draws are consecutive values from one per-row random stream keyed by `seed` and the
    row's position, so the result is reproducible for a fixed `seed` and independent of Polars chunking and
    thread scheduling. `samples(size=1)` matches `sample` for the same seed, and growing `size` extends each
    row's array without changing the existing draws.

    A row with a null parameter yields a null array (not an array of null elements), produced natively by
    the plugin via the output's outer validity; an invalid parameterisation raises.

    Naming follows `sample`: `"samples"` with all-constant parameters, the first parameter
    expression's root name otherwise.
    """
    if size <= 0:
        msg = f"size must be a positive integer, got {size}"
        raise ValueError(msg)
    out = self._samples(size=size, seed=seed)
    return out.alias("samples") if self._scalar_kwargs is not None else out

cdf

cdf(value: float | IntoExprColumn) -> Expr

Cumulative distribution function, P(X <= value). Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def cdf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Cumulative distribution function, `P(X <= value)`. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._cdf(v))

log_cdf

log_cdf(value: float | IntoExprColumn) -> Expr

Natural logarithm of the cdf. Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def log_cdf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Natural logarithm of the cdf. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._log_cdf(v))

sf

sf(value: float | IntoExprColumn) -> Expr

Survival function, P(X > value) = 1 - cdf(value). Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def sf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Survival function, `P(X > value) = 1 - cdf(value)`. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._sf(v))

log_sf

log_sf(value: float | IntoExprColumn) -> Expr

Natural logarithm of the survival function. Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def log_sf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Natural logarithm of the survival function. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._log_sf(v))

ppf

ppf(quantile: float | IntoExprColumn) -> Expr

Percent point function (inverse cdf).

A quantile outside [0, 1] yields null.

Nulls are propagated and a NaN quantile yields NaN, matching scipy.

Source code in polars_stats/distributions/_base.py
def ppf(self, quantile: float | IntoExprColumn) -> pl.Expr:
    """Percent point function (inverse cdf).

    A `quantile` outside `[0, 1]` yields **null**.

    Nulls are propagated and a `NaN` quantile yields `NaN`, matching scipy.
    """
    q = as_expr(quantile)
    return propagate_null_and_nan(q, self._ppf(q))

isf

isf(quantile: float | IntoExprColumn) -> Expr

Inverse survival function, the value x with sf(x) == quantile.

Same domain contract as ppf, with the endpoints reversed: quantile outside [0, 1] yields null, nulls propagate, NaN yields NaN.

Source code in polars_stats/distributions/_base.py
def isf(self, quantile: float | IntoExprColumn) -> pl.Expr:
    """Inverse survival function, the value `x` with `sf(x) == quantile`.

    Same domain contract as `ppf`, with the endpoints reversed: `quantile` outside `[0, 1]` yields
    null, nulls propagate, `NaN` yields `NaN`.
    """
    q = as_expr(quantile)
    return propagate_null_and_nan(q, self._isf(q))

std

std() -> Expr

Standard deviation, sqrt(variance).

Source code in polars_stats/distributions/_base.py
def std(self) -> pl.Expr:
    """Standard deviation, `sqrt(variance)`."""
    return self.variance().sqrt()

median

median() -> Expr

Median, ppf(0.5).

Source code in polars_stats/distributions/_base.py
def median(self) -> pl.Expr:
    """Median, `ppf(0.5)`."""
    return self.ppf(0.5)

pmf

pmf(value: float | IntoExprColumn) -> Expr

Probability mass function, P(X = value). Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def pmf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Probability mass function, `P(X = value)`. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._pmf(v))

log_pmf

log_pmf(value: float | IntoExprColumn) -> Expr

Natural logarithm of the pmf. Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def log_pmf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Natural logarithm of the pmf. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._log_pmf(v))

mean

mean() -> Expr

Expected value, p.

Source code in polars_stats/distributions/_bernoulli.py
def mean(self) -> pl.Expr:
    """Expected value, ``p``."""
    return self._checked_p

variance

variance() -> Expr

Variance, p * (1 - p).

Source code in polars_stats/distributions/_bernoulli.py
def variance(self) -> pl.Expr:
    """Variance, ``p * (1 - p)``."""
    p = self._checked_p
    return p * (1 - p)

entropy

entropy() -> Expr

Shannon entropy, -p * log(p) - (1 - p) * log1p(-p).

Uses the convention 0 * log 0 = 0 so the result is 0 at the degenerate endpoints p in {0, 1}. The second term goes through log1p for the reason in _log_pmf.

Source code in polars_stats/distributions/_bernoulli.py
def entropy(self) -> pl.Expr:
    """Shannon entropy, ``-p * log(p) - (1 - p) * log1p(-p)``.

    Uses the convention ``0 * log 0 = 0`` so the result is ``0`` at the degenerate endpoints ``p in {0, 1}``.
    The second term goes through ``log1p`` for the reason in ``_log_pmf``.
    """
    p = self._checked_p
    q = 1 - p

    return pl.when((p == 0) | (p == 1)).then(0.0).otherwise(-p * p.log() - q * (-p).log1p())

Binomial

Binomial(n: int | IntoExprColumn, p: float | IntoExprColumn)

Bases: DiscreteDistribution

Binomial distribution: number of successes in n trials, each with success probability p.

Equivalent to scipy.stats.binom(n, p). The argument order differs from statrs (Binomial(p, n)); this class follows scipy's (n, p).

Parameters:

Name Type Description Default
n int | IntoExprColumn

Number of trials, an integer >= 0. Either a Python int or an IntoExprColumn (pl.Expr, pl.Series or column name str) carrying one count per row.

required
p float | IntoExprColumn

Success probability in [0, 1]. Either a Python float or an IntoExprColumn.

required

Neither parameter is validated at construction: a negative n or a p outside [0, 1] raises InvalidOperation (a ComputeError) when a method is evaluated, identically to an invalid column row. Construction rejects only wrong types (TypeError). Null parameters propagate to null.

Source code in polars_stats/distributions/_binomial.py
def __init__(self, n: int | IntoExprColumn, p: float | IntoExprColumn) -> None:
    self._n = coerce_n(n, name="n")
    self._p = coerce_param(p, name="p")
    self._scalar_kwargs = scalar_kwargs(n=scalar_int(n), p=scalar_float(p))

sample

sample(seed: int | None = None) -> Expr

Draw one random variate per row.

Returns a column with one variate per input row, in the distribution's element dtype (Float64, UInt64 or Boolean). Output length follows the surrounding context (frame length under select / with_columns, partition length under over / group_by), and each row's draw is derived from a per-row sub-seed mixed from seed and the row's position, so the result is independent of Polars chunking and thread scheduling.

A row with an invalid parameter raises; a row with a null parameter yields null. The output is named "sample" when every parameter is constant (the fast path); with any column-valued parameter the name follows the first parameter expression (polars root-name semantics, so .name.* modifiers keep working).

Source code in polars_stats/distributions/_base.py
def sample(self, seed: int | None = None) -> pl.Expr:
    """Draw one random variate per row.

    Returns a column with one variate per input row, in the distribution's element dtype
    (`Float64`, `UInt64` or `Boolean`). Output length follows the surrounding context (frame length
    under `select` / `with_columns`, partition length under `over` / `group_by`), and each row's draw
    is derived from a per-row sub-seed mixed from `seed` and the row's position, so the result is
    independent of Polars chunking and thread scheduling.

    A row with an invalid parameter raises; a row with a null parameter yields null. The output is
    named `"sample"` when every parameter is constant (the fast path); with any column-valued
    parameter the name follows the first parameter expression (polars root-name semantics, so
    `.name.*` modifiers keep working).
    """
    if self._scalar_kwargs is not None:
        return register_plugin(
            f"{self._plugin_prefix}_sample_scalar",
            (ROW_INDEX_EXPR,),
            kwargs={"seed": seed, **self._scalar_kwargs},
        ).alias("sample")
    return register_plugin(
        f"{self._plugin_prefix}_sample", (*self._param_exprs, ROW_INDEX_EXPR), kwargs={"seed": seed}
    )

samples

samples(size: int, seed: int | None = None) -> Expr

Draw size random variates per row, returning Array(inner=<element dtype>, shape=size).

Each row's size draws are consecutive values from one per-row random stream keyed by seed and the row's position, so the result is reproducible for a fixed seed and independent of Polars chunking and thread scheduling. samples(size=1) matches sample for the same seed, and growing size extends each row's array without changing the existing draws.

A row with a null parameter yields a null array (not an array of null elements), produced natively by the plugin via the output's outer validity; an invalid parameterisation raises.

Naming follows sample: "samples" with all-constant parameters, the first parameter expression's root name otherwise.

Source code in polars_stats/distributions/_base.py
def samples(self, size: int, seed: int | None = None) -> pl.Expr:
    """Draw `size` random variates per row, returning `Array(inner=<element dtype>, shape=size)`.

    Each row's `size` draws are consecutive values from one per-row random stream keyed by `seed` and the
    row's position, so the result is reproducible for a fixed `seed` and independent of Polars chunking and
    thread scheduling. `samples(size=1)` matches `sample` for the same seed, and growing `size` extends each
    row's array without changing the existing draws.

    A row with a null parameter yields a null array (not an array of null elements), produced natively by
    the plugin via the output's outer validity; an invalid parameterisation raises.

    Naming follows `sample`: `"samples"` with all-constant parameters, the first parameter
    expression's root name otherwise.
    """
    if size <= 0:
        msg = f"size must be a positive integer, got {size}"
        raise ValueError(msg)
    out = self._samples(size=size, seed=seed)
    return out.alias("samples") if self._scalar_kwargs is not None else out

cdf

cdf(value: float | IntoExprColumn) -> Expr

Cumulative distribution function, P(X <= value). Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def cdf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Cumulative distribution function, `P(X <= value)`. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._cdf(v))

log_cdf

log_cdf(value: float | IntoExprColumn) -> Expr

Natural logarithm of the cdf. Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def log_cdf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Natural logarithm of the cdf. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._log_cdf(v))

sf

sf(value: float | IntoExprColumn) -> Expr

Survival function, P(X > value) = 1 - cdf(value). Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def sf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Survival function, `P(X > value) = 1 - cdf(value)`. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._sf(v))

log_sf

log_sf(value: float | IntoExprColumn) -> Expr

Natural logarithm of the survival function. Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def log_sf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Natural logarithm of the survival function. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._log_sf(v))

ppf

ppf(quantile: float | IntoExprColumn) -> Expr

Percent point function (inverse cdf).

A quantile outside [0, 1] yields null.

Nulls are propagated and a NaN quantile yields NaN, matching scipy.

Source code in polars_stats/distributions/_base.py
def ppf(self, quantile: float | IntoExprColumn) -> pl.Expr:
    """Percent point function (inverse cdf).

    A `quantile` outside `[0, 1]` yields **null**.

    Nulls are propagated and a `NaN` quantile yields `NaN`, matching scipy.
    """
    q = as_expr(quantile)
    return propagate_null_and_nan(q, self._ppf(q))

isf

isf(quantile: float | IntoExprColumn) -> Expr

Inverse survival function, the value x with sf(x) == quantile.

Same domain contract as ppf, with the endpoints reversed: quantile outside [0, 1] yields null, nulls propagate, NaN yields NaN.

Source code in polars_stats/distributions/_base.py
def isf(self, quantile: float | IntoExprColumn) -> pl.Expr:
    """Inverse survival function, the value `x` with `sf(x) == quantile`.

    Same domain contract as `ppf`, with the endpoints reversed: `quantile` outside `[0, 1]` yields
    null, nulls propagate, `NaN` yields `NaN`.
    """
    q = as_expr(quantile)
    return propagate_null_and_nan(q, self._isf(q))

std

std() -> Expr

Standard deviation, sqrt(variance).

Source code in polars_stats/distributions/_base.py
def std(self) -> pl.Expr:
    """Standard deviation, `sqrt(variance)`."""
    return self.variance().sqrt()

median

median() -> Expr

Median, ppf(0.5).

Source code in polars_stats/distributions/_base.py
def median(self) -> pl.Expr:
    """Median, `ppf(0.5)`."""
    return self.ppf(0.5)

pmf

pmf(value: float | IntoExprColumn) -> Expr

Probability mass function, P(X = value). Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def pmf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Probability mass function, `P(X = value)`. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._pmf(v))

log_pmf

log_pmf(value: float | IntoExprColumn) -> Expr

Natural logarithm of the pmf. Nulls and NaNs in value are propagated.

Source code in polars_stats/distributions/_base.py
def log_pmf(self, value: float | IntoExprColumn) -> pl.Expr:
    """Natural logarithm of the pmf. Nulls and NaNs in `value` are propagated."""
    v = as_expr(value)
    return propagate_null_and_nan(v, self._log_pmf(v))

mean

mean() -> Expr

Expected value, n * p.

Source code in polars_stats/distributions/_binomial.py
def mean(self) -> pl.Expr:
    """Expected value, ``n * p``."""
    return self._moment(self._n * self._p)

variance

variance() -> Expr

Variance, n * p * (1 - p).

Source code in polars_stats/distributions/_binomial.py
def variance(self) -> pl.Expr:
    """Variance, ``n * p * (1 - p)``."""
    return self._moment(self._n * self._p * (1 - self._p))

entropy

entropy() -> Expr

Shannon entropy in nats, the exact support sum -sum_k pmf(k) log pmf(k).

0 at the degenerate endpoints p in {0, 1}.

Source code in polars_stats/distributions/_binomial.py
def entropy(self) -> pl.Expr:
    """Shannon entropy in nats, the exact support sum ``-sum_k pmf(k) log pmf(k)``.

    ``0`` at the degenerate endpoints ``p in {0, 1}``.
    """
    # Unlike ``mean`` / ``variance`` there is no closed form, so the sum is evaluated by ``binomial_entropy``
    # in Rust. For column parameters that runs once per row; for scalar parameters it is computed **once** on
    # length-1 inputs and broadcast to length-n behind the ``_moment`` validity gate, so a constant's support sum is
    # not re-evaluated on every row.
    if self._scalar_kwargs is None:
        return register_plugin("binomial_entropy", (self._n, self._p))
    return self._moment(register_plugin("binomial_entropy", self._scalar_lit_args()))