Path: blob/main/crates/polars-expr/src/reduce/var_std.rs
6940 views
use std::marker::PhantomData;12use num_traits::AsPrimitive;3use polars_compute::moment::VarState;4use polars_core::with_match_physical_numeric_polars_type;56use super::*;78pub fn new_var_std_reduction(dtype: DataType, is_std: bool, ddof: u8) -> Box<dyn GroupedReduction> {9use DataType::*;10use VecGroupedReduction as VGR;11match dtype {12Boolean => Box::new(VGR::new(dtype, BoolVarStdReducer { is_std, ddof })),13_ if dtype.is_primitive_numeric() => {14with_match_physical_numeric_polars_type!(dtype.to_physical(), |$T| {15Box::new(VGR::new(dtype, VarStdReducer::<$T> {16is_std,17ddof,18needs_cast: false,19_phantom: PhantomData,20}))21})22},23#[cfg(feature = "dtype-decimal")]24Decimal(_, _) => Box::new(VGR::new(25dtype,26VarStdReducer::<Float64Type> {27is_std,28ddof,29needs_cast: true,30_phantom: PhantomData,31},32)),33Duration(..) => todo!(),34_ => unimplemented!(),35}36}3738struct VarStdReducer<T> {39is_std: bool,40ddof: u8,41needs_cast: bool,42_phantom: PhantomData<T>,43}4445impl<T> Clone for VarStdReducer<T> {46fn clone(&self) -> Self {47Self {48is_std: self.is_std,49ddof: self.ddof,50needs_cast: self.needs_cast,51_phantom: PhantomData,52}53}54}5556impl<T: PolarsNumericType> Reducer for VarStdReducer<T> {57type Dtype = T;58type Value = VarState;5960fn init(&self) -> Self::Value {61VarState::default()62}6364fn cast_series<'a>(&self, s: &'a Series) -> Cow<'a, Series> {65if self.needs_cast {66Cow::Owned(s.cast(&DataType::Float64).unwrap())67} else {68Cow::Borrowed(s)69}70}7172fn combine(&self, a: &mut Self::Value, b: &Self::Value) {73a.combine(b)74}7576#[inline(always)]77fn reduce_one(&self, a: &mut Self::Value, b: Option<T::Native>, _seq_id: u64) {78if let Some(x) = b {79a.insert_one(x.as_());80}81}8283fn reduce_ca(&self, v: &mut Self::Value, ca: &ChunkedArray<Self::Dtype>, _seq_id: u64) {84for arr in ca.downcast_iter() {85v.combine(&polars_compute::moment::var(arr))86}87}8889fn finish(90&self,91v: Vec<Self::Value>,92m: Option<Bitmap>,93_dtype: &DataType,94) -> PolarsResult<Series> {95assert!(m.is_none());96let ca: Float64Chunked = v97.into_iter()98.map(|s| {99let var = s.finalize(self.ddof);100if self.is_std { var.map(f64::sqrt) } else { var }101})102.collect_ca(PlSmallStr::EMPTY);103Ok(ca.into_series())104}105}106107#[derive(Clone)]108struct BoolVarStdReducer {109is_std: bool,110ddof: u8,111}112113impl Reducer for BoolVarStdReducer {114type Dtype = BooleanType;115type Value = (usize, usize);116117fn init(&self) -> Self::Value {118(0, 0)119}120121fn combine(&self, a: &mut Self::Value, b: &Self::Value) {122a.0 += b.0;123a.1 += b.1;124}125126#[inline(always)]127fn reduce_one(&self, a: &mut Self::Value, b: Option<bool>, _seq_id: u64) {128a.0 += b.unwrap_or(false) as usize;129a.1 += b.is_some() as usize;130}131132fn reduce_ca(&self, v: &mut Self::Value, ca: &ChunkedArray<Self::Dtype>, _seq_id: u64) {133v.0 += ca.sum().unwrap_or(0) as usize;134v.1 += ca.len() - ca.null_count();135}136137fn finish(138&self,139v: Vec<Self::Value>,140m: Option<Bitmap>,141_dtype: &DataType,142) -> PolarsResult<Series> {143assert!(m.is_none());144let ca: Float64Chunked = v145.into_iter()146.map(|v| {147if v.1 <= self.ddof as usize {148return None;149}150151let sum = v.0 as f64; // Both the sum and sum-of-squares, letting us simplify.152let n = v.1;153let var = sum * (1.0 - sum / n as f64) / ((n - self.ddof as usize) as f64);154if self.is_std {155Some(var.sqrt())156} else {157Some(var)158}159})160.collect_ca(PlSmallStr::EMPTY);161Ok(ca.into_series())162}163}164165166