blob: b8e52f849a7ccfc8c010250a0779e9eef008d3e9 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//! [`VarianceSample`]: variance sample aggregations.
//! [`VariancePopulation`]: variance population aggregations.
use arrow::datatypes::{FieldRef, Float64Type};
use arrow::{
array::{Array, ArrayRef, BooleanArray, Float64Array, UInt64Array},
buffer::NullBuffer,
datatypes::{DataType, Field},
};
use datafusion_common::cast::{as_float64_array, as_uint64_array};
use datafusion_common::{Result, ScalarValue};
use datafusion_expr::{
Accumulator, AggregateUDFImpl, Documentation, GroupsAccumulator, Signature,
Volatility,
function::{AccumulatorArgs, StateFieldsArgs},
utils::format_state_name,
};
use datafusion_functions_aggregate_common::utils::GenericDistinctBuffer;
use datafusion_functions_aggregate_common::{
aggregate::groups_accumulator::accumulate::accumulate, stats::StatsType,
};
use datafusion_macros::user_doc;
use std::mem::{size_of, size_of_val};
use std::{fmt::Debug, sync::Arc};
make_udaf_expr_and_func!(
VarianceSample,
var_sample,
expression,
"Computes the sample variance.",
var_samp_udaf
);
make_udaf_expr_and_func!(
VariancePopulation,
var_pop,
expression,
"Computes the population variance.",
var_pop_udaf
);
#[user_doc(
doc_section(label = "General Functions"),
description = "Returns the statistical sample variance of a set of numbers.",
syntax_example = "var(expression)",
standard_argument(name = "expression", prefix = "Numeric")
)]
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct VarianceSample {
signature: Signature,
aliases: Vec<String>,
}
impl Default for VarianceSample {
fn default() -> Self {
Self::new()
}
}
impl VarianceSample {
pub fn new() -> Self {
Self {
aliases: vec![String::from("var_sample"), String::from("var_samp")],
signature: Signature::exact(vec![DataType::Float64], Volatility::Immutable),
}
}
}
impl AggregateUDFImpl for VarianceSample {
fn name(&self) -> &str {
"var"
}
fn signature(&self) -> &Signature {
&self.signature
}
fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
Ok(DataType::Float64)
}
fn state_fields(&self, args: StateFieldsArgs) -> Result<Vec<FieldRef>> {
let name = args.name;
match args.is_distinct {
false => Ok(vec![
Field::new(format_state_name(name, "count"), DataType::UInt64, true),
Field::new(format_state_name(name, "mean"), DataType::Float64, true),
Field::new(format_state_name(name, "m2"), DataType::Float64, true),
]
.into_iter()
.map(Arc::new)
.collect()),
true => {
let field = Field::new_list_field(DataType::Float64, true);
let state_name = "distinct_var";
Ok(vec![
Field::new(
format_state_name(name, state_name),
DataType::List(Arc::new(field)),
true,
)
.into(),
])
}
}
}
fn accumulator(&self, acc_args: AccumulatorArgs) -> Result<Box<dyn Accumulator>> {
if acc_args.is_distinct {
return Ok(Box::new(DistinctVarianceAccumulator::new(
StatsType::Sample,
)));
}
Ok(Box::new(VarianceAccumulator::try_new(StatsType::Sample)?))
}
fn aliases(&self) -> &[String] {
&self.aliases
}
fn groups_accumulator_supported(&self, acc_args: AccumulatorArgs) -> bool {
!acc_args.is_distinct
}
fn create_groups_accumulator(
&self,
_args: AccumulatorArgs,
) -> Result<Box<dyn GroupsAccumulator>> {
Ok(Box::new(VarianceGroupsAccumulator::new(StatsType::Sample)))
}
fn documentation(&self) -> Option<&Documentation> {
self.doc()
}
}
#[user_doc(
doc_section(label = "General Functions"),
description = "Returns the statistical population variance of a set of numbers.",
syntax_example = "var_pop(expression)",
standard_argument(name = "expression", prefix = "Numeric")
)]
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct VariancePopulation {
signature: Signature,
aliases: Vec<String>,
}
impl Default for VariancePopulation {
fn default() -> Self {
Self::new()
}
}
impl VariancePopulation {
pub fn new() -> Self {
Self {
aliases: vec![String::from("var_population")],
signature: Signature::exact(vec![DataType::Float64], Volatility::Immutable),
}
}
}
impl AggregateUDFImpl for VariancePopulation {
fn name(&self) -> &str {
"var_pop"
}
fn signature(&self) -> &Signature {
&self.signature
}
fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
Ok(DataType::Float64)
}
fn state_fields(&self, args: StateFieldsArgs) -> Result<Vec<FieldRef>> {
match args.is_distinct {
false => {
let name = args.name;
Ok(vec![
Field::new(format_state_name(name, "count"), DataType::UInt64, true),
Field::new(format_state_name(name, "mean"), DataType::Float64, true),
Field::new(format_state_name(name, "m2"), DataType::Float64, true),
]
.into_iter()
.map(Arc::new)
.collect())
}
true => {
let field = Field::new_list_field(DataType::Float64, true);
let state_name = "distinct_var";
Ok(vec![
Field::new(
format_state_name(args.name, state_name),
DataType::List(Arc::new(field)),
true,
)
.into(),
])
}
}
}
fn accumulator(&self, acc_args: AccumulatorArgs) -> Result<Box<dyn Accumulator>> {
if acc_args.is_distinct {
return Ok(Box::new(DistinctVarianceAccumulator::new(
StatsType::Population,
)));
}
Ok(Box::new(VarianceAccumulator::try_new(
StatsType::Population,
)?))
}
fn aliases(&self) -> &[String] {
&self.aliases
}
fn groups_accumulator_supported(&self, acc_args: AccumulatorArgs) -> bool {
!acc_args.is_distinct
}
fn create_groups_accumulator(
&self,
_args: AccumulatorArgs,
) -> Result<Box<dyn GroupsAccumulator>> {
Ok(Box::new(VarianceGroupsAccumulator::new(
StatsType::Population,
)))
}
fn documentation(&self) -> Option<&Documentation> {
self.doc()
}
}
/// An accumulator to compute variance
/// The algorithm used is an online implementation and numerically stable. It is based on this paper:
/// Welford, B. P. (1962). "Note on a method for calculating corrected sums of squares and products".
/// Technometrics. 4 (3): 419–420. doi:10.2307/1266577. JSTOR 1266577.
///
/// The algorithm has been analyzed here:
/// Ling, Robert F. (1974). "Comparison of Several Algorithms for Computing Sample Means and Variances".
/// Journal of the American Statistical Association. 69 (348): 859–866. doi:10.2307/2286154. JSTOR 2286154.
#[derive(Debug)]
pub struct VarianceAccumulator {
m2: f64,
mean: f64,
count: u64,
stats_type: StatsType,
}
impl VarianceAccumulator {
/// Creates a new `VarianceAccumulator`
pub fn try_new(s_type: StatsType) -> Result<Self> {
Ok(Self {
m2: 0_f64,
mean: 0_f64,
count: 0_u64,
stats_type: s_type,
})
}
pub fn get_count(&self) -> u64 {
self.count
}
pub fn get_mean(&self) -> f64 {
self.mean
}
pub fn get_m2(&self) -> f64 {
self.m2
}
}
#[inline]
fn merge(
count: u64,
mean: f64,
m2: f64,
count2: u64,
mean2: f64,
m22: f64,
) -> (u64, f64, f64) {
debug_assert!(count != 0 || count2 != 0, "Cannot merge two empty states");
let new_count = count + count2;
let new_mean =
mean * count as f64 / new_count as f64 + mean2 * count2 as f64 / new_count as f64;
let delta = mean - mean2;
let new_m2 =
m2 + m22 + delta * delta * count as f64 * count2 as f64 / new_count as f64;
(new_count, new_mean, new_m2)
}
#[inline]
fn update(count: u64, mean: f64, m2: f64, value: f64) -> (u64, f64, f64) {
let new_count = count + 1;
let delta1 = value - mean;
let new_mean = delta1 / new_count as f64 + mean;
let delta2 = value - new_mean;
let new_m2 = m2 + delta1 * delta2;
(new_count, new_mean, new_m2)
}
/// Inverse of [`update`]: removes a previously accumulated value. Retracting
/// from a state with one or zero values resets the state to empty.
#[inline]
fn retract(count: u64, mean: f64, m2: f64, value: f64) -> (u64, f64, f64) {
if count <= 1 {
return (0, 0.0, 0.0);
}
let new_count = count - 1;
let delta1 = mean - value;
let new_mean = delta1 / new_count as f64 + mean;
let delta2 = new_mean - value;
let new_m2 = m2 - delta1 * delta2;
(new_count, new_mean, new_m2)
}
impl Accumulator for VarianceAccumulator {
fn state(&mut self) -> Result<Vec<ScalarValue>> {
Ok(vec![
ScalarValue::from(self.count),
ScalarValue::from(self.mean),
ScalarValue::from(self.m2),
])
}
fn update_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
let arr = as_float64_array(&values[0])?;
for value in arr.iter().flatten() {
(self.count, self.mean, self.m2) =
update(self.count, self.mean, self.m2, value)
}
Ok(())
}
fn retract_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
let arr = as_float64_array(&values[0])?;
for value in arr.iter().flatten() {
(self.count, self.mean, self.m2) =
retract(self.count, self.mean, self.m2, value)
}
Ok(())
}
fn merge_batch(&mut self, states: &[ArrayRef]) -> Result<()> {
let counts = as_uint64_array(&states[0])?;
let means = as_float64_array(&states[1])?;
let m2s = as_float64_array(&states[2])?;
for i in 0..counts.len() {
let c = counts.value(i);
if c == 0_u64 {
continue;
}
(self.count, self.mean, self.m2) = merge(
self.count,
self.mean,
self.m2,
c,
means.value(i),
m2s.value(i),
)
}
Ok(())
}
fn evaluate(&mut self) -> Result<ScalarValue> {
let count = match self.stats_type {
StatsType::Population => self.count,
StatsType::Sample => {
if self.count > 0 {
self.count - 1
} else {
self.count
}
}
};
Ok(ScalarValue::Float64(match self.count {
0 => None,
1 => {
if let StatsType::Population = self.stats_type {
Some(0.0)
} else {
None
}
}
_ => Some(self.m2 / count as f64),
}))
}
fn size(&self) -> usize {
size_of_val(self)
}
fn supports_retract_batch(&self) -> bool {
true
}
}
#[derive(Debug)]
pub struct VarianceGroupsAccumulator {
m2s: Vec<f64>,
means: Vec<f64>,
counts: Vec<u64>,
stats_type: StatsType,
}
impl VarianceGroupsAccumulator {
pub fn new(s_type: StatsType) -> Self {
Self {
m2s: Vec::new(),
means: Vec::new(),
counts: Vec::new(),
stats_type: s_type,
}
}
fn resize(&mut self, total_num_groups: usize) {
self.m2s.resize(total_num_groups, 0.0);
self.means.resize(total_num_groups, 0.0);
self.counts.resize(total_num_groups, 0);
}
fn merge<F>(
group_indices: &[usize],
counts: &UInt64Array,
means: &Float64Array,
m2s: &Float64Array,
_opt_filter: Option<&BooleanArray>,
mut value_fn: F,
) where
F: FnMut(usize, u64, f64, f64) + Send,
{
assert_eq!(counts.null_count(), 0);
assert_eq!(means.null_count(), 0);
assert_eq!(m2s.null_count(), 0);
group_indices
.iter()
.zip(counts.values().iter())
.zip(means.values().iter())
.zip(m2s.values().iter())
.for_each(|(((&group_index, &count), &mean), &m2)| {
value_fn(group_index, count, mean, m2);
});
}
pub fn variance(
&mut self,
emit_to: datafusion_expr::EmitTo,
) -> (Vec<f64>, NullBuffer) {
let mut counts = emit_to.take_needed(&mut self.counts);
// means are only needed for updating m2s and are not needed for the final result.
// But we still need to take them to ensure the internal state is consistent.
let _ = emit_to.take_needed(&mut self.means);
let m2s = emit_to.take_needed(&mut self.m2s);
if let StatsType::Sample = self.stats_type {
counts.iter_mut().for_each(|count| {
*count = count.saturating_sub(1);
});
}
let nulls = NullBuffer::from_iter(counts.iter().map(|&count| count != 0));
let variance = m2s
.iter()
.zip(counts)
.map(|(m2, count)| m2 / count as f64)
.collect();
(variance, nulls)
}
}
impl GroupsAccumulator for VarianceGroupsAccumulator {
fn update_batch(
&mut self,
values: &[ArrayRef],
group_indices: &[usize],
opt_filter: Option<&BooleanArray>,
total_num_groups: usize,
) -> Result<()> {
assert_eq!(values.len(), 1, "single argument to update_batch");
let values = as_float64_array(&values[0])?;
self.resize(total_num_groups);
accumulate(group_indices, values, opt_filter, |group_index, value| {
let (new_count, new_mean, new_m2) = update(
self.counts[group_index],
self.means[group_index],
self.m2s[group_index],
value,
);
self.counts[group_index] = new_count;
self.means[group_index] = new_mean;
self.m2s[group_index] = new_m2;
});
Ok(())
}
fn merge_batch(
&mut self,
values: &[ArrayRef],
group_indices: &[usize],
total_num_groups: usize,
) -> Result<()> {
assert_eq!(values.len(), 3, "two arguments to merge_batch");
// first batch is counts, second is partial means, third is partial m2s
let partial_counts = as_uint64_array(&values[0])?;
let partial_means = as_float64_array(&values[1])?;
let partial_m2s = as_float64_array(&values[2])?;
self.resize(total_num_groups);
Self::merge(
group_indices,
partial_counts,
partial_means,
partial_m2s,
None,
|group_index, partial_count, partial_mean, partial_m2| {
if partial_count == 0 {
return;
}
let (new_count, new_mean, new_m2) = merge(
self.counts[group_index],
self.means[group_index],
self.m2s[group_index],
partial_count,
partial_mean,
partial_m2,
);
self.counts[group_index] = new_count;
self.means[group_index] = new_mean;
self.m2s[group_index] = new_m2;
},
);
Ok(())
}
fn evaluate(&mut self, emit_to: datafusion_expr::EmitTo) -> Result<ArrayRef> {
let (variances, nulls) = self.variance(emit_to);
Ok(Arc::new(Float64Array::new(variances.into(), Some(nulls))))
}
fn state(&mut self, emit_to: datafusion_expr::EmitTo) -> Result<Vec<ArrayRef>> {
let counts = emit_to.take_needed(&mut self.counts);
let means = emit_to.take_needed(&mut self.means);
let m2s = emit_to.take_needed(&mut self.m2s);
Ok(vec![
Arc::new(UInt64Array::new(counts.into(), None)),
Arc::new(Float64Array::new(means.into(), None)),
Arc::new(Float64Array::new(m2s.into(), None)),
])
}
fn convert_to_state(
&self,
values: &[ArrayRef],
opt_filter: Option<&BooleanArray>,
) -> Result<Vec<ArrayRef>> {
assert_eq!(values.len(), 1, "single argument to convert_to_state");
let values = as_float64_array(&values[0])?;
let len = values.len();
let mut counts = Vec::with_capacity(len);
let mut means = Vec::with_capacity(len);
let mut m2s = Vec::with_capacity(len);
for row in 0..len {
if values.is_valid(row)
&& opt_filter
.is_none_or(|filter| filter.is_valid(row) && filter.value(row))
{
counts.push(1);
means.push(values.value(row));
} else {
counts.push(0);
means.push(0.0);
}
m2s.push(0.0);
}
Ok(vec![
Arc::new(UInt64Array::new(counts.into(), None)),
Arc::new(Float64Array::new(means.into(), None)),
Arc::new(Float64Array::new(m2s.into(), None)),
])
}
fn size(&self) -> usize {
self.m2s.capacity() * size_of::<f64>()
+ self.means.capacity() * size_of::<f64>()
+ self.counts.capacity() * size_of::<u64>()
}
}
#[derive(Debug)]
pub struct DistinctVarianceAccumulator {
distinct_values: GenericDistinctBuffer<Float64Type>,
stat_type: StatsType,
}
impl DistinctVarianceAccumulator {
pub fn new(stat_type: StatsType) -> Self {
Self {
distinct_values: GenericDistinctBuffer::<Float64Type>::new(DataType::Float64),
stat_type,
}
}
}
impl Accumulator for DistinctVarianceAccumulator {
fn update_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
self.distinct_values.update_batch(values)
}
fn evaluate(&mut self) -> Result<ScalarValue> {
let values = self
.distinct_values
.values
.iter()
.map(|v| v.0)
.collect::<Vec<_>>();
let count = match self.stat_type {
StatsType::Sample => {
if !values.is_empty() {
values.len() - 1
} else {
0
}
}
StatsType::Population => values.len(),
};
let mean = values.iter().sum::<f64>() / values.len() as f64;
let m2 = values.iter().map(|x| (x - mean) * (x - mean)).sum::<f64>();
Ok(ScalarValue::Float64(match values.len() {
0 => None,
1 => match self.stat_type {
StatsType::Population => Some(0.0),
StatsType::Sample => None,
},
_ => Some(m2 / count as f64),
}))
}
fn size(&self) -> usize {
size_of_val(self) + self.distinct_values.size()
}
fn state(&mut self) -> Result<Vec<ScalarValue>> {
self.distinct_values.state()
}
fn merge_batch(&mut self, states: &[ArrayRef]) -> Result<()> {
self.distinct_values.merge_batch(states)
}
}
#[cfg(test)]
mod tests {
use datafusion_expr::EmitTo;
use super::*;
#[test]
fn update_batch_ignores_nulls() -> Result<()> {
// An array with nulls must accumulate the same values as a dense
// array of its non-null values.
let dense: ArrayRef = Arc::new(Float64Array::from(vec![1.0, 2.0, 3.0, 4.0]));
let sparse: ArrayRef = Arc::new(Float64Array::from(vec![
Some(1.0),
None,
Some(2.0),
Some(3.0),
None,
Some(4.0),
]));
let mut dense_acc = VarianceAccumulator::try_new(StatsType::Sample)?;
dense_acc.update_batch(std::slice::from_ref(&dense))?;
let mut sparse_acc = VarianceAccumulator::try_new(StatsType::Sample)?;
sparse_acc.update_batch(std::slice::from_ref(&sparse))?;
// Sample variance of {1, 2, 3, 4} is 5/3 (all steps are exact in f64).
assert_eq!(dense_acc.evaluate()?, ScalarValue::Float64(Some(5.0 / 3.0)));
assert_eq!(dense_acc.evaluate()?, sparse_acc.evaluate()?);
Ok(())
}
#[test]
fn retract_batch_ignores_nulls() -> Result<()> {
let values: ArrayRef = Arc::new(Float64Array::from(vec![1.0, 2.0, 3.0, 4.0]));
let dense_retract: ArrayRef = Arc::new(Float64Array::from(vec![1.0, 2.0]));
let sparse_retract: ArrayRef =
Arc::new(Float64Array::from(vec![Some(1.0), None, Some(2.0)]));
let mut dense_acc = VarianceAccumulator::try_new(StatsType::Sample)?;
dense_acc.update_batch(std::slice::from_ref(&values))?;
dense_acc.retract_batch(std::slice::from_ref(&dense_retract))?;
let mut sparse_acc = VarianceAccumulator::try_new(StatsType::Sample)?;
sparse_acc.update_batch(std::slice::from_ref(&values))?;
sparse_acc.retract_batch(std::slice::from_ref(&sparse_retract))?;
// Sample variance of the remaining {3, 4} is 0.5 (all steps are exact
// in f64).
assert_eq!(dense_acc.evaluate()?, ScalarValue::Float64(Some(0.5)));
assert_eq!(dense_acc.evaluate()?, sparse_acc.evaluate()?);
Ok(())
}
#[test]
fn retract_batch_resets_when_underflowing() -> Result<()> {
// Retracting more values than were accumulated resets to the empty
// state, with or without nulls in the retracted batch.
let values: ArrayRef = Arc::new(Float64Array::from(vec![1.0, 2.0]));
let dense_retract: ArrayRef = Arc::new(Float64Array::from(vec![1.0, 2.0, 3.0]));
let sparse_retract: ArrayRef = Arc::new(Float64Array::from(vec![
Some(1.0),
None,
Some(2.0),
Some(3.0),
]));
for retract in [&dense_retract, &sparse_retract] {
let mut acc = VarianceAccumulator::try_new(StatsType::Sample)?;
acc.update_batch(std::slice::from_ref(&values))?;
acc.retract_batch(std::slice::from_ref(retract))?;
assert_eq!(acc.get_count(), 0);
assert_eq!(acc.evaluate()?, ScalarValue::Float64(None));
}
Ok(())
}
#[test]
fn test_groups_accumulator_merge_empty_states() -> Result<()> {
let state_1 = vec![
Arc::new(UInt64Array::from(vec![0])) as ArrayRef,
Arc::new(Float64Array::from(vec![0.0])),
Arc::new(Float64Array::from(vec![0.0])),
];
let state_2 = vec![
Arc::new(UInt64Array::from(vec![2])) as ArrayRef,
Arc::new(Float64Array::from(vec![1.0])),
Arc::new(Float64Array::from(vec![1.0])),
];
let mut acc = VarianceGroupsAccumulator::new(StatsType::Sample);
acc.merge_batch(&state_1, &[0], 1)?;
acc.merge_batch(&state_2, &[0], 1)?;
let result = acc.evaluate(EmitTo::All)?;
let result = result.as_any().downcast_ref::<Float64Array>().unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result.value(0), 1.0);
Ok(())
}
#[test]
fn convert_to_state_roundtrips_through_merge() -> Result<()> {
let values = Arc::new(Float64Array::from(vec![
Some(1.0),
Some(2.0),
None,
Some(4.0),
Some(8.0),
Some(16.0),
Some(32.0),
])) as ArrayRef;
let filter = BooleanArray::from(vec![
Some(true),
Some(false),
Some(true),
None,
Some(true),
Some(true),
Some(true),
]);
let group_indices = vec![0, 1, 0, 1, 0, 0, 0];
let mut direct = VarianceGroupsAccumulator::new(StatsType::Sample);
direct.update_batch(
std::slice::from_ref(&values),
&group_indices,
Some(&filter),
2,
)?;
let direct = direct.evaluate(EmitTo::All)?;
let converter = VarianceGroupsAccumulator::new(StatsType::Sample);
let state =
converter.convert_to_state(std::slice::from_ref(&values), Some(&filter))?;
let mut merged = VarianceGroupsAccumulator::new(StatsType::Sample);
merged.merge_batch(&state, &group_indices, 2)?;
let merged = merged.evaluate(EmitTo::All)?;
let direct = direct.as_any().downcast_ref::<Float64Array>().unwrap();
let merged = merged.as_any().downcast_ref::<Float64Array>().unwrap();
assert_eq!(direct.len(), merged.len());
for row in 0..direct.len() {
assert_eq!(direct.is_null(row), merged.is_null(row));
if direct.is_valid(row) {
assert!((direct.value(row) - merged.value(row)).abs() < 1e-12);
}
}
Ok(())
}
#[test]
fn convert_to_state_preserves_empty_and_filtered_rows() -> Result<()> {
let converter = VarianceGroupsAccumulator::new(StatsType::Sample);
let empty_values =
Arc::new(Float64Array::from(Vec::<Option<f64>>::new())) as ArrayRef;
let state =
converter.convert_to_state(std::slice::from_ref(&empty_values), None)?;
for state_array in &state {
assert_eq!(state_array.len(), 0);
assert_eq!(state_array.null_count(), 0);
}
let values =
Arc::new(Float64Array::from(vec![Some(1.0), Some(2.0), None])) as ArrayRef;
let filter = BooleanArray::from(vec![Some(false), None, Some(false)]);
let group_indices = vec![0, 1, 0];
let state =
converter.convert_to_state(std::slice::from_ref(&values), Some(&filter))?;
for state_array in &state {
assert_eq!(state_array.len(), values.len());
assert_eq!(state_array.null_count(), 0);
}
let counts = state[0].as_any().downcast_ref::<UInt64Array>().unwrap();
assert_eq!(counts, &UInt64Array::from(vec![0, 0, 0]));
let mut merged = VarianceGroupsAccumulator::new(StatsType::Sample);
merged.merge_batch(&state, &group_indices, 2)?;
let result = merged.evaluate(EmitTo::All)?;
let result = result.as_any().downcast_ref::<Float64Array>().unwrap();
assert_eq!(result.len(), 2);
assert_eq!(result.null_count(), 2);
Ok(())
}
}