Path: blob/main/crates/polars-compute/src/rolling/no_nulls/moment.rs
8503 views
#![allow(unsafe_op_in_unsafe_fn)]1use num_traits::{FromPrimitive, ToPrimitive};2use polars_error::polars_ensure;34pub use super::super::moment::*;5use super::*;67pub fn rolling_var<T>(8values: &[T],9window_size: usize,10min_periods: usize,11center: bool,12weights: Option<&[f64]>,13params: Option<RollingFnParams>,14) -> PolarsResult<ArrayRef>15where16T: NativeType + Float + IsFloat + ToPrimitive + FromPrimitive + AddAssign,17{18let offset_fn = match center {19true => det_offsets_center,20false => det_offsets,21};22match weights {23None => rolling_apply_agg_window::<MomentWindow<_, VarianceMoment>, _, _, _>(24values,25window_size,26min_periods,27offset_fn,28params,29),30Some(weights) => {31// Validate and standardize the weights like we do for the mean. This definition is fine32// because frequency weights and unbiasing don't make sense for rolling operations.33let mut wts = no_nulls::coerce_weights(weights);34let wsum = wts.iter().fold(T::zero(), |acc, x| acc + *x);35polars_ensure!(36wsum != T::zero(),37ComputeError: "Weighted variance is undefined if weights sum to 0"38);39wts.iter_mut().for_each(|w| *w = *w / wsum);40super::rolling_apply_weights(41values,42window_size,43min_periods,44offset_fn,45compute_var_weights,46&wts,47center,48)49},50}51}5253pub fn rolling_skew<T>(54values: &[T],55window_size: usize,56min_periods: usize,57center: bool,58params: Option<RollingFnParams>,59) -> PolarsResult<ArrayRef>60where61T: NativeType + Float + IsFloat + ToPrimitive + FromPrimitive + AddAssign,62{63let offset_fn = match center {64true => det_offsets_center,65false => det_offsets,66};67rolling_apply_agg_window::<MomentWindow<_, SkewMoment>, _, _, _>(68values,69window_size,70min_periods,71offset_fn,72params,73)74}7576pub fn rolling_kurtosis<T>(77values: &[T],78window_size: usize,79min_periods: usize,80center: bool,81params: Option<RollingFnParams>,82) -> PolarsResult<ArrayRef>83where84T: NativeType + Float + IsFloat + ToPrimitive + FromPrimitive + AddAssign,85{86let offset_fn = match center {87true => det_offsets_center,88false => det_offsets,89};90rolling_apply_agg_window::<MomentWindow<_, KurtosisMoment>, _, _, _>(91values,92window_size,93min_periods,94offset_fn,95params,96)97}9899#[cfg(test)]100mod test {101use super::*;102103#[test]104fn test_rolling_var() {105let values = &[1.0f64, 5.0, 3.0, 4.0];106107let out = rolling_var(values, 2, 2, false, None, None).unwrap();108let out = out.as_any().downcast_ref::<PrimitiveArray<f64>>().unwrap();109let out = out.into_iter().map(|v| v.copied()).collect::<Vec<_>>();110assert_eq!(out, &[None, Some(8.0), Some(2.0), Some(0.5)]);111112let testpars = Some(RollingFnParams::Var(RollingVarParams { ddof: 0 }));113let out = rolling_var(values, 2, 2, false, None, testpars).unwrap();114let out = out.as_any().downcast_ref::<PrimitiveArray<f64>>().unwrap();115let out = out.into_iter().map(|v| v.copied()).collect::<Vec<_>>();116assert_eq!(out, &[None, Some(4.0), Some(1.0), Some(0.25)]);117118let out = rolling_var(values, 2, 1, false, None, None).unwrap();119let out = out.as_any().downcast_ref::<PrimitiveArray<f64>>().unwrap();120let out = out.into_iter().map(|v| v.copied()).collect::<Vec<_>>();121// we cannot compare nans, so we compare the string values122assert_eq!(123format!("{:?}", out.as_slice()),124format!("{:?}", &[None, Some(8.0), Some(2.0), Some(0.5)])125);126// test nan handling.127let values = &[-10.0, 2.0, 3.0, f64::nan(), 5.0, 6.0, 7.0];128let out = rolling_var(values, 3, 3, false, None, None).unwrap();129let out = out.as_any().downcast_ref::<PrimitiveArray<f64>>().unwrap();130let out = out.into_iter().map(|v| v.copied()).collect::<Vec<_>>();131// we cannot compare nans, so we compare the string values132assert_eq!(133format!("{:?}", out.as_slice()),134format!(135"{:?}",136&[137None,138None,139Some(52.33333333333333),140Some(f64::nan()),141Some(f64::nan()),142Some(f64::nan()),143Some(0.9999999999999911)144]145)146);147}148}149150151