Skip to content

Commit 05871c3

Browse files
aggregate_fn: Return NaN instead of null for mean of all-null input (#8365)
`Mean::finalize_scalar` returned null when the count was zero, while the array `finalize` path computes `sum/count = 0/0 = NaN` for the same input. A mean over an all-null group therefore gave different results depending on which accumulator we're using. https://github.com/vortex-data/vortex/blob/90d743356722a8d5ca7e39053229654778bacf46/vortex-array/src/aggregate_fn/fns/mean/mod.rs#L85-L93 Since nulls are skipped during accumulation (as in standard SQL aggregation), an all-null input is an empty mean. Both paths now let the division produce NaN. Note this only matters for the count = 0 case: sum overflow still returns null. Signed-off-by: Dimitar Dimitrov <dimitar@spiraldb.com>
1 parent 9383c35 commit 05871c3

1 file changed

Lines changed: 5 additions & 3 deletions

File tree

  • vortex-array/src/aggregate_fn/fns/mean

vortex-array/src/aggregate_fn/fns/mean/mod.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ impl BinaryCombined for Mean {
104104
let sum = sum_cast.as_primitive().typed_value::<f64>();
105105
let count = count_cast.as_primitive().typed_value::<f64>();
106106
let value = match (sum, count) {
107-
(None, _) | (_, None) | (_, Some(0.0)) => return Ok(Scalar::null(target)), // Sum overflowed
107+
(None, _) | (_, None) => return Ok(Scalar::null(target)), // Sum overflowed
108+
// A count of zero yields 0/0 = NaN, matching the array `finalize` path: nulls are
109+
// skipped during accumulation, so an all-null input is an empty mean, not null.
108110
(Some(s), Some(c)) => s / c,
109111
};
110112
Ok(Scalar::primitive(value, Nullability::Nullable))
@@ -230,11 +232,11 @@ mod tests {
230232
}
231233

232234
#[test]
233-
fn mean_all_null_returns_null() -> VortexResult<()> {
235+
fn mean_all_null_returns_nan() -> VortexResult<()> {
234236
let array = PrimitiveArray::from_option_iter::<f64, _>([None, None, None]).into_array();
235237
let mut ctx = LEGACY_SESSION.create_execution_ctx();
236238
let result = mean(&array, &mut ctx)?;
237-
assert_eq!(result.as_primitive().as_::<f64>(), None);
239+
assert!(result.as_primitive().as_::<f64>().is_some_and(f64::is_nan));
238240
Ok(())
239241
}
240242

0 commit comments

Comments
 (0)