@@ -326,6 +326,23 @@ fn update(count: u64, mean: f64, m2: f64, value: f64) -> (u64, f64, f64) {
326326 ( new_count, new_mean, new_m2)
327327}
328328
329+ /// Inverse of [`update`]: removes a previously accumulated value. Retracting
330+ /// from a state with one or zero values resets the state to empty.
331+ #[ inline]
332+ fn retract ( count : u64 , mean : f64 , m2 : f64 , value : f64 ) -> ( u64 , f64 , f64 ) {
333+ if count <= 1 {
334+ return ( 0 , 0.0 , 0.0 ) ;
335+ }
336+
337+ let new_count = count - 1 ;
338+ let delta1 = mean - value;
339+ let new_mean = delta1 / new_count as f64 + mean;
340+ let delta2 = new_mean - value;
341+ let new_m2 = m2 - delta1 * delta2;
342+
343+ ( new_count, new_mean, new_m2)
344+ }
345+
329346impl Accumulator for VarianceAccumulator {
330347 fn state ( & mut self ) -> Result < Vec < ScalarValue > > {
331348 Ok ( vec ! [
@@ -348,22 +365,8 @@ impl Accumulator for VarianceAccumulator {
348365 fn retract_batch ( & mut self , values : & [ ArrayRef ] ) -> Result < ( ) > {
349366 let arr = as_float64_array ( & values[ 0 ] ) ?;
350367 for value in arr. iter ( ) . flatten ( ) {
351- if self . count <= 1 {
352- self . count = 0 ;
353- self . mean = 0.0 ;
354- self . m2 = 0.0 ;
355- continue ;
356- }
357-
358- let new_count = self . count - 1 ;
359- let delta1 = self . mean - value;
360- let new_mean = delta1 / new_count as f64 + self . mean ;
361- let delta2 = new_mean - value;
362- let new_m2 = self . m2 - delta1 * delta2;
363-
364- self . count -= 1 ;
365- self . mean = new_mean;
366- self . m2 = new_m2;
368+ ( self . count , self . mean , self . m2 ) =
369+ retract ( self . count , self . mean , self . m2 , value)
367370 }
368371
369372 Ok ( ( ) )
@@ -691,6 +694,75 @@ mod tests {
691694
692695 use super :: * ;
693696
697+ #[ test]
698+ fn update_batch_ignores_nulls ( ) -> Result < ( ) > {
699+ // An array with nulls must accumulate the same values as a dense
700+ // array of its non-null values.
701+ let dense: ArrayRef = Arc :: new ( Float64Array :: from ( vec ! [ 1.0 , 2.0 , 3.0 , 4.0 ] ) ) ;
702+ let sparse: ArrayRef = Arc :: new ( Float64Array :: from ( vec ! [
703+ Some ( 1.0 ) ,
704+ None ,
705+ Some ( 2.0 ) ,
706+ Some ( 3.0 ) ,
707+ None ,
708+ Some ( 4.0 ) ,
709+ ] ) ) ;
710+
711+ let mut dense_acc = VarianceAccumulator :: try_new ( StatsType :: Sample ) ?;
712+ dense_acc. update_batch ( std:: slice:: from_ref ( & dense) ) ?;
713+ let mut sparse_acc = VarianceAccumulator :: try_new ( StatsType :: Sample ) ?;
714+ sparse_acc. update_batch ( std:: slice:: from_ref ( & sparse) ) ?;
715+
716+ // Sample variance of {1, 2, 3, 4} is 5/3 (all steps are exact in f64).
717+ assert_eq ! ( dense_acc. evaluate( ) ?, ScalarValue :: Float64 ( Some ( 5.0 / 3.0 ) ) ) ;
718+ assert_eq ! ( dense_acc. evaluate( ) ?, sparse_acc. evaluate( ) ?) ;
719+ Ok ( ( ) )
720+ }
721+
722+ #[ test]
723+ fn retract_batch_ignores_nulls ( ) -> Result < ( ) > {
724+ let values: ArrayRef = Arc :: new ( Float64Array :: from ( vec ! [ 1.0 , 2.0 , 3.0 , 4.0 ] ) ) ;
725+ let dense_retract: ArrayRef = Arc :: new ( Float64Array :: from ( vec ! [ 1.0 , 2.0 ] ) ) ;
726+ let sparse_retract: ArrayRef =
727+ Arc :: new ( Float64Array :: from ( vec ! [ Some ( 1.0 ) , None , Some ( 2.0 ) ] ) ) ;
728+
729+ let mut dense_acc = VarianceAccumulator :: try_new ( StatsType :: Sample ) ?;
730+ dense_acc. update_batch ( std:: slice:: from_ref ( & values) ) ?;
731+ dense_acc. retract_batch ( std:: slice:: from_ref ( & dense_retract) ) ?;
732+ let mut sparse_acc = VarianceAccumulator :: try_new ( StatsType :: Sample ) ?;
733+ sparse_acc. update_batch ( std:: slice:: from_ref ( & values) ) ?;
734+ sparse_acc. retract_batch ( std:: slice:: from_ref ( & sparse_retract) ) ?;
735+
736+ // Sample variance of the remaining {3, 4} is 0.5 (all steps are exact
737+ // in f64).
738+ assert_eq ! ( dense_acc. evaluate( ) ?, ScalarValue :: Float64 ( Some ( 0.5 ) ) ) ;
739+ assert_eq ! ( dense_acc. evaluate( ) ?, sparse_acc. evaluate( ) ?) ;
740+ Ok ( ( ) )
741+ }
742+
743+ #[ test]
744+ fn retract_batch_resets_when_underflowing ( ) -> Result < ( ) > {
745+ // Retracting more values than were accumulated resets to the empty
746+ // state, with or without nulls in the retracted batch.
747+ let values: ArrayRef = Arc :: new ( Float64Array :: from ( vec ! [ 1.0 , 2.0 ] ) ) ;
748+ let dense_retract: ArrayRef = Arc :: new ( Float64Array :: from ( vec ! [ 1.0 , 2.0 , 3.0 ] ) ) ;
749+ let sparse_retract: ArrayRef = Arc :: new ( Float64Array :: from ( vec ! [
750+ Some ( 1.0 ) ,
751+ None ,
752+ Some ( 2.0 ) ,
753+ Some ( 3.0 ) ,
754+ ] ) ) ;
755+
756+ for retract in [ & dense_retract, & sparse_retract] {
757+ let mut acc = VarianceAccumulator :: try_new ( StatsType :: Sample ) ?;
758+ acc. update_batch ( std:: slice:: from_ref ( & values) ) ?;
759+ acc. retract_batch ( std:: slice:: from_ref ( retract) ) ?;
760+ assert_eq ! ( acc. get_count( ) , 0 ) ;
761+ assert_eq ! ( acc. evaluate( ) ?, ScalarValue :: Float64 ( None ) ) ;
762+ }
763+ Ok ( ( ) )
764+ }
765+
694766 #[ test]
695767 fn test_groups_accumulator_merge_empty_states ( ) -> Result < ( ) > {
696768 let state_1 = vec ! [
0 commit comments