Skip to content

Commit

Permalink
feat: support min_periods for temporal rolling aggregations (pola-rs#…
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcoGorelli authored Jan 2, 2024
1 parent acb0afc commit a6d6293
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ fn rolling_agg<T>(
Duration,
&[i64],
ClosedWindow,
usize,
TimeUnit,
Option<&TimeZone>,
DynArgs,
Expand Down Expand Up @@ -87,6 +88,7 @@ where
duration,
by,
closed_window,
options.min_periods,
tu,
options.tz,
options.fn_params,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use super::*;
pub(crate) fn rolling_apply_agg_window<'a, Agg, T, O>(
values: &'a [T],
offsets: O,
min_periods: usize,
params: DynArgs,
) -> PolarsResult<ArrayRef>
where
Expand All @@ -32,7 +33,10 @@ where
result.map(|(start, len)| {
let end = start + len;

if start == end {
// On the Python side, if `min_periods` wasn't specified, it is set to
// `1`. In that case, this condition is the same as checking
// `if start == end`.
if len < (min_periods as IdxSize) {
None
} else {
// safety:
Expand All @@ -52,6 +56,7 @@ pub(crate) fn rolling_min<T>(
period: Duration,
time: &[i64],
closed_window: ClosedWindow,
min_periods: usize,
tu: TimeUnit,
tz: Option<&TimeZone>,
_params: DynArgs,
Expand All @@ -64,7 +69,7 @@ where
Some(tz) => group_by_values_iter(period, time, closed_window, tu, tz.parse::<Tz>().ok()),
_ => group_by_values_iter(period, time, closed_window, tu, None),
}?;
rolling_apply_agg_window::<no_nulls::MinWindow<_>, _, _>(values, offset_iter, None)
rolling_apply_agg_window::<no_nulls::MinWindow<_>, _, _>(values, offset_iter, min_periods, None)
}

#[allow(clippy::too_many_arguments)]
Expand All @@ -73,6 +78,7 @@ pub(crate) fn rolling_max<T>(
period: Duration,
time: &[i64],
closed_window: ClosedWindow,
min_periods: usize,
tu: TimeUnit,
tz: Option<&TimeZone>,
_params: DynArgs,
Expand All @@ -85,7 +91,7 @@ where
Some(tz) => group_by_values_iter(period, time, closed_window, tu, tz.parse::<Tz>().ok()),
_ => group_by_values_iter(period, time, closed_window, tu, None),
}?;
rolling_apply_agg_window::<no_nulls::MaxWindow<_>, _, _>(values, offset_iter, None)
rolling_apply_agg_window::<no_nulls::MaxWindow<_>, _, _>(values, offset_iter, min_periods, None)
}

#[allow(clippy::too_many_arguments)]
Expand All @@ -94,6 +100,7 @@ pub(crate) fn rolling_sum<T>(
period: Duration,
time: &[i64],
closed_window: ClosedWindow,
min_periods: usize,
tu: TimeUnit,
tz: Option<&TimeZone>,
_params: DynArgs,
Expand All @@ -106,7 +113,7 @@ where
Some(tz) => group_by_values_iter(period, time, closed_window, tu, tz.parse::<Tz>().ok()),
_ => group_by_values_iter(period, time, closed_window, tu, None),
}?;
rolling_apply_agg_window::<no_nulls::SumWindow<_>, _, _>(values, offset_iter, None)
rolling_apply_agg_window::<no_nulls::SumWindow<_>, _, _>(values, offset_iter, min_periods, None)
}

#[allow(clippy::too_many_arguments)]
Expand All @@ -115,6 +122,7 @@ pub(crate) fn rolling_mean<T>(
period: Duration,
time: &[i64],
closed_window: ClosedWindow,
min_periods: usize,
tu: TimeUnit,
tz: Option<&TimeZone>,
_params: DynArgs,
Expand All @@ -127,7 +135,12 @@ where
Some(tz) => group_by_values_iter(period, time, closed_window, tu, tz.parse::<Tz>().ok()),
_ => group_by_values_iter(period, time, closed_window, tu, None),
}?;
rolling_apply_agg_window::<no_nulls::MeanWindow<_>, _, _>(values, offset_iter, None)
rolling_apply_agg_window::<no_nulls::MeanWindow<_>, _, _>(
values,
offset_iter,
min_periods,
None,
)
}

#[allow(clippy::too_many_arguments)]
Expand All @@ -136,6 +149,7 @@ pub(crate) fn rolling_var<T>(
period: Duration,
time: &[i64],
closed_window: ClosedWindow,
min_periods: usize,
tu: TimeUnit,
tz: Option<&TimeZone>,
params: DynArgs,
Expand All @@ -148,7 +162,12 @@ where
Some(tz) => group_by_values_iter(period, time, closed_window, tu, tz.parse::<Tz>().ok()),
_ => group_by_values_iter(period, time, closed_window, tu, None),
}?;
rolling_apply_agg_window::<no_nulls::VarWindow<_>, _, _>(values, offset_iter, params)
rolling_apply_agg_window::<no_nulls::VarWindow<_>, _, _>(
values,
offset_iter,
min_periods,
params,
)
}

#[allow(clippy::too_many_arguments)]
Expand All @@ -157,6 +176,7 @@ pub(crate) fn rolling_quantile<T>(
period: Duration,
time: &[i64],
closed_window: ClosedWindow,
min_periods: usize,
tu: TimeUnit,
tz: Option<&TimeZone>,
params: DynArgs,
Expand All @@ -169,5 +189,10 @@ where
Some(tz) => group_by_values_iter(period, time, closed_window, tu, tz.parse::<Tz>().ok()),
_ => group_by_values_iter(period, time, closed_window, tu, None),
}?;
rolling_apply_agg_window::<no_nulls::QuantileWindow<_>, _, _>(values, offset_iter, params)
rolling_apply_agg_window::<no_nulls::QuantileWindow<_>, _, _>(
values,
offset_iter,
min_periods,
params,
)
}
50 changes: 40 additions & 10 deletions py-polars/polars/expr/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -5701,7 +5701,10 @@ def rolling_min(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
by
Expand Down Expand Up @@ -5911,7 +5914,10 @@ def rolling_max(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
by
Expand Down Expand Up @@ -6144,7 +6150,10 @@ def rolling_mean(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
by
Expand Down Expand Up @@ -6387,7 +6396,10 @@ def rolling_sum(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
by
Expand Down Expand Up @@ -6617,7 +6629,10 @@ def rolling_std(
relative contribution of each value in a window to the output.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
by
Expand Down Expand Up @@ -6860,7 +6875,10 @@ def rolling_var(
relative contribution of each value in a window to the output.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
by
Expand Down Expand Up @@ -7102,7 +7120,10 @@ def rolling_median(
relative contribution of each value in a window to the output.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
by
Expand Down Expand Up @@ -7267,7 +7288,10 @@ def rolling_quantile(
relative contribution of each value in a window to the output.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
by
Expand Down Expand Up @@ -7469,7 +7493,10 @@ def rolling_map(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window.
Expand Down Expand Up @@ -9431,7 +9458,10 @@ def rolling_apply(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
Expand Down
50 changes: 40 additions & 10 deletions py-polars/polars/series/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -5379,7 +5379,10 @@ def rolling_min(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
Expand Down Expand Up @@ -5435,7 +5438,10 @@ def rolling_max(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
Expand Down Expand Up @@ -5491,7 +5497,10 @@ def rolling_mean(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
Expand Down Expand Up @@ -5547,7 +5556,10 @@ def rolling_sum(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
Expand Down Expand Up @@ -5604,7 +5616,10 @@ def rolling_std(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
ddof
Expand Down Expand Up @@ -5664,7 +5679,10 @@ def rolling_var(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
ddof
Expand Down Expand Up @@ -5724,7 +5742,10 @@ def rolling_map(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window.
Expand Down Expand Up @@ -5769,7 +5790,10 @@ def rolling_median(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
Expand Down Expand Up @@ -5834,7 +5858,10 @@ def rolling_quantile(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
Expand Down Expand Up @@ -7042,7 +7069,10 @@ def rolling_apply(
elementwise with the values in the window.
min_periods
The number of values in the window that should be non-null before computing
a result. If None, it will be set equal to window size.
a result. If None, it will be set equal to:
- the window size, if `window_size` is a fixed integer
- 1, if `window_size` is a dynamic temporal size
center
Set the labels at the center of the window
Expand Down
Loading

0 comments on commit a6d6293

Please sign in to comment.