pola-rs/polars · error

weights not yet supported on array with null values

Error message

weights not yet supported on array with null values

What it means

The null-handling rolling kernels in polars-compute (nulls::rolling_mean and siblings) implement only unweighted aggregation. rolling_mean starts with an explicit panic guard: if weights is Some while the input array contains nulls, it panics 'weights not yet supported on array with null values' rather than returning silently wrong numbers. Weighted rolling works only on the no-nulls code path.

Source

Thrown at crates/polars-compute/src/rolling/nulls/mean.rs:25

    window_size: usize,
    min_periods: usize,
    center: bool,
    weights: Option<&[f64]>,
    _params: Option<RollingFnParams>,
) -> ArrayRef
where
    T: NativeType
        + IsFloat
        + PartialOrd
        + Add<Output = T>
        + Sub<Output = T>
        + NumCast
        + AddAssign
        + SubAssign
        + Div<Output = T>,
{
    if weights.is_some() {
        panic!("weights not yet supported on array with null values")
    }
    if center {
        rolling_apply_agg_window::<MeanWindow<T>, _, _, _>(
            arr.values().as_slice(),
            arr.validity().as_ref().unwrap(),
            window_size,
            min_periods,
            det_offsets_center,
            None,
        )
    } else {
        rolling_apply_agg_window::<MeanWindow<T>, _, _, _>(
            arr.values().as_slice(),
            arr.validity().as_ref().unwrap(),
            window_size,
            min_periods,
            det_offsets,
            None,

View on GitHub (pinned to 9b5d73fd00)

Solutions

  1. Fill or drop nulls before the rolling call: s.fill_null(0.0).rolling_mean(...) (note filling changes the statistic) or s.drop_nulls() (shifts window alignment)
  2. Omit the weights argument - the unweighted null-handling path is fully implemented
  3. Compute a weighted mean from unweighted ops after filling: rolling_sum(x*w) / rolling_sum(w)
  4. If exact weighted-with-nulls semantics are required, implement it upstream (weight window aggregation by validity) or file a feature request

Example fix

# before
s.rolling_mean(window_size=3, weights=[0.2, 0.3, 0.5])  # panics when s has nulls
# after
s_filled = s.fill_null(0.0)
s_filled.rolling_mean(window_size=3, weights=[0.2, 0.3, 0.5])
Defensive patterns

Strategy: validation

Validate before calling

def safe_rolling_mean(s: pl.Series, window_size: int, weights=None, min_periods=1):
    if weights is not None and s.null_count() > 0:
        raise ValueError(
            "weights + null values unsupported: fill or drop nulls, or drop weights"
        )
    return s.rolling_mean(window_size, weights=weights, min_periods=min_periods)

Try / catch

try:
    out = s.rolling_mean(window_size=3, weights=w)
except pl.exceptions.PanicException:
    out = s.fill_null(0.0).rolling_mean(window_size=3, weights=w)  # document the bias

Prevention

When it happens

Trigger: s.rolling_mean(window_size=3, weights=[0.2, 0.3, 0.5]) - or .rolling_mean(..., weights=...) in an expression - on a Series or group that contains at least one null value; polars dispatches to the nulls path (null_count > 0) and the weight guard fires.

Common situations: Time-series with missing observations: code developed on gap-free test data passes weights fine, production data with nulls panics; group_by().agg(rolling_mean with weights) where some groups contain nulls; lazy queries where nulls appear only after a join.

Related errors


AI-assisted analysis of pola-rs/polars@9b5d73fd00 (2026-08-19). Data as JSON: /api/errors/6016ee0e6b9248d5. Report an issue: GitHub.