datafusion_functions_nested/
array_avg.rs1use crate::utils::make_scalar_function;
21use arrow::array::{Array, ArrayRef, Float64Array, OffsetSizeTrait};
22use arrow::datatypes::{
23 DataType,
24 DataType::{FixedSizeList, LargeList, List, Null},
25 Field,
26};
27use datafusion_common::cast::{as_float64_array, as_generic_list_array};
28use datafusion_common::utils::{ListCoercion, coerced_type_with_base_type_only};
29use datafusion_common::{Result, internal_err, plan_err, utils::take_function_args};
30use datafusion_expr::{
31 ColumnarValue, Documentation, ScalarFunctionArgs, ScalarUDFImpl, Signature,
32 Volatility,
33};
34use datafusion_macros::user_doc;
35use std::sync::Arc;
36
37make_udf_expr_and_func!(
38 ArrayAvg,
39 array_avg,
40 array,
41 "returns the arithmetic mean of elements in a numeric array.",
42 array_avg_udf
43);
44
45#[user_doc(
46 doc_section(label = "Array Functions"),
47 description = "Returns the arithmetic mean (sum divided by count) of the elements of the input array. NULL elements are skipped (per SQL aggregate convention) and excluded from the count. Returns NULL if the input row is NULL, every element is NULL, or the array is empty.",
48 syntax_example = "array_avg(array)",
49 sql_example = r#"```sql
50> select array_avg([1.0, 2.0, 3.0]);
51+----------------------------+
52| array_avg(List([1.0,2.0,3.0])) |
53+----------------------------+
54| 2.0 |
55+----------------------------+
56```"#,
57 argument(
58 name = "array",
59 description = "Array expression. Can be a constant, column, or function, and any combination of array operators."
60 )
61)]
62#[derive(Debug, PartialEq, Eq, Hash)]
63pub struct ArrayAvg {
64 signature: Signature,
65 aliases: Vec<String>,
66}
67
68impl Default for ArrayAvg {
69 fn default() -> Self {
70 Self::new()
71 }
72}
73
74impl ArrayAvg {
75 pub fn new() -> Self {
76 Self {
77 signature: Signature::user_defined(Volatility::Immutable),
78 aliases: vec!["list_avg".to_string()],
79 }
80 }
81}
82
83impl ScalarUDFImpl for ArrayAvg {
84 fn name(&self) -> &str {
85 "array_avg"
86 }
87
88 fn signature(&self) -> &Signature {
89 &self.signature
90 }
91
92 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
93 Ok(DataType::Float64)
94 }
95
96 fn coerce_types(&self, arg_types: &[DataType]) -> Result<Vec<DataType>> {
97 let [arg_type] = take_function_args(self.name(), arg_types)?;
98 let coercion = Some(&ListCoercion::FixedSizedListToList);
99
100 if !matches!(arg_type, Null | List(_) | LargeList(_) | FixedSizeList(..)) {
101 return plan_err!("{} does not support type {arg_type}", self.name());
102 }
103
104 let coerced = if matches!(arg_type, Null) {
105 List(Arc::new(Field::new_list_field(DataType::Float64, true)))
106 } else {
107 coerced_type_with_base_type_only(arg_type, &DataType::Float64, coercion)
108 };
109
110 Ok(vec![coerced])
111 }
112
113 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
114 make_scalar_function(array_avg_inner)(&args.args)
115 }
116
117 fn aliases(&self) -> &[String] {
118 &self.aliases
119 }
120
121 fn documentation(&self) -> Option<&Documentation> {
122 self.doc()
123 }
124}
125
126fn array_avg_inner(args: &[ArrayRef]) -> Result<ArrayRef> {
127 let [array] = take_function_args("array_avg", args)?;
128 match array.data_type() {
129 List(_) => general_array_avg::<i32>(array),
130 LargeList(_) => general_array_avg::<i64>(array),
131 arg_type => {
132 internal_err!("array_avg received unexpected type after coercion: {arg_type}")
133 }
134 }
135}
136
137fn general_array_avg<O: OffsetSizeTrait>(array: &ArrayRef) -> Result<ArrayRef> {
138 let list_array = as_generic_list_array::<O>(array)?;
139 let values = as_float64_array(list_array.values())?;
140 let offsets = list_array.value_offsets();
141
142 let mut builder = Float64Array::builder(list_array.len());
143
144 for row in 0..list_array.len() {
145 if list_array.is_null(row) {
146 builder.append_null();
147 continue;
148 }
149
150 let start = offsets[row].as_usize();
151 let end = offsets[row + 1].as_usize();
152
153 let mut sum = 0.0_f64;
158 let mut count: u64 = 0;
159 for i in start..end {
160 if values.is_valid(i) {
161 sum += values.value(i);
162 count += 1;
163 }
164 }
165
166 if count > 0 {
167 builder.append_value(sum / count as f64);
168 } else {
169 builder.append_null();
170 }
171 }
172
173 Ok(Arc::new(builder.finish()))
174}