1use std::cmp::Ordering;
19use std::fmt::{Debug, Formatter};
20use std::mem::{size_of, size_of_val};
21use std::sync::Arc;
22
23use arrow::array::{
24 ArrowNumericType, BooleanArray, ListArray, PrimitiveArray, PrimitiveBuilder,
25 downcast_integer,
26};
27use arrow::buffer::{OffsetBuffer, ScalarBuffer};
28use arrow::{
29 array::{ArrayRef, AsArray},
30 datatypes::{
31 DataType, Decimal128Type, Decimal256Type, Field, Float16Type, Float32Type,
32 Float64Type,
33 },
34};
35
36use arrow::array::Array;
37use arrow::array::ArrowNativeTypeOp;
38use arrow::datatypes::{
39 ArrowNativeType, ArrowPrimitiveType, Decimal32Type, Decimal64Type, FieldRef,
40};
41
42use datafusion_common::types::{NativeType, logical_float64};
43use datafusion_common::{
44 DataFusionError, Result, ScalarValue, assert_eq_or_internal_err,
45 internal_datafusion_err,
46};
47use datafusion_expr::function::StateFieldsArgs;
48use datafusion_expr::{
49 Accumulator, AggregateUDFImpl, Coercion, Documentation, Signature, TypeSignature,
50 TypeSignatureClass, Volatility, function::AccumulatorArgs, utils::format_state_name,
51};
52use datafusion_expr::{EmitTo, GroupsAccumulator};
53use datafusion_functions_aggregate_common::aggregate::groups_accumulator::accumulate::accumulate;
54use datafusion_functions_aggregate_common::aggregate::groups_accumulator::nulls::filtered_null_mask;
55use datafusion_functions_aggregate_common::utils::{GenericDistinctBuffer, Hashable};
56use datafusion_macros::user_doc;
57use std::collections::HashMap;
58
59make_udaf_expr_and_func!(
60 Median,
61 median,
62 expression,
63 "Computes the median of a set of numbers",
64 median_udaf
65);
66
67#[user_doc(
68 doc_section(label = "General Functions"),
69 description = "Returns the median value in the specified column.",
70 syntax_example = "median(expression)",
71 sql_example = r#"```sql
72> SELECT median(column_name) FROM table_name;
73+----------------------+
74| median(column_name) |
75+----------------------+
76| 45.5 |
77+----------------------+
78```"#,
79 standard_argument(name = "expression", prefix = "The")
80)]
81#[derive(PartialEq, Eq, Hash, Debug)]
90pub struct Median {
91 signature: Signature,
92}
93
94impl Default for Median {
95 fn default() -> Self {
96 Self::new()
97 }
98}
99
100impl Median {
101 pub fn new() -> Self {
102 Self {
103 signature: Signature::one_of(
107 vec![
108 TypeSignature::Coercible(vec![Coercion::new_exact(
109 TypeSignatureClass::Decimal,
110 )]),
111 TypeSignature::Coercible(vec![Coercion::new_exact(
112 TypeSignatureClass::Float,
113 )]),
114 TypeSignature::Coercible(vec![Coercion::new_implicit(
115 TypeSignatureClass::Native(logical_float64()),
116 vec![TypeSignatureClass::Integer],
117 NativeType::Float64,
118 )]),
119 ],
120 Volatility::Immutable,
121 ),
122 }
123 }
124}
125
126impl AggregateUDFImpl for Median {
127 fn name(&self) -> &str {
128 "median"
129 }
130
131 fn signature(&self) -> &Signature {
132 &self.signature
133 }
134
135 fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
136 Ok(arg_types[0].clone())
137 }
138
139 fn state_fields(&self, args: StateFieldsArgs) -> Result<Vec<FieldRef>> {
140 let field = Field::new_list_field(args.input_fields[0].data_type().clone(), true);
142 let state_name = if args.is_distinct {
143 "distinct_median"
144 } else {
145 "median"
146 };
147
148 Ok(vec![
149 Field::new(
150 format_state_name(args.name, state_name),
151 DataType::List(Arc::new(field)),
152 true,
153 )
154 .into(),
155 ])
156 }
157
158 fn accumulator(&self, acc_args: AccumulatorArgs) -> Result<Box<dyn Accumulator>> {
159 macro_rules! helper {
160 ($t:ty, $dt:expr) => {
161 if acc_args.is_distinct {
162 Ok(Box::new(DistinctMedianAccumulator::<$t> {
163 data_type: $dt.clone(),
164 distinct_values: GenericDistinctBuffer::new($dt),
165 }))
166 } else {
167 Ok(Box::new(MedianAccumulator::<$t> {
168 data_type: $dt.clone(),
169 all_values: vec![],
170 }))
171 }
172 };
173 }
174
175 let dt = acc_args.expr_fields[0].data_type().clone();
176 downcast_integer! {
177 dt => (helper, dt),
178 DataType::Float16 => helper!(Float16Type, dt),
179 DataType::Float32 => helper!(Float32Type, dt),
180 DataType::Float64 => helper!(Float64Type, dt),
181 DataType::Decimal32(_, _) => helper!(Decimal32Type, dt),
182 DataType::Decimal64(_, _) => helper!(Decimal64Type, dt),
183 DataType::Decimal128(_, _) => helper!(Decimal128Type, dt),
184 DataType::Decimal256(_, _) => helper!(Decimal256Type, dt),
185 _ => Err(DataFusionError::NotImplemented(format!(
186 "MedianAccumulator not supported for {} with {}",
187 acc_args.name,
188 dt,
189 ))),
190 }
191 }
192
193 fn groups_accumulator_supported(&self, args: AccumulatorArgs) -> bool {
194 !args.is_distinct
195 }
196
197 fn create_groups_accumulator(
198 &self,
199 args: AccumulatorArgs,
200 ) -> Result<Box<dyn GroupsAccumulator>> {
201 let num_args = args.exprs.len();
202 assert_eq_or_internal_err!(
203 num_args,
204 1,
205 "median should only have 1 arg, but found num args:{}",
206 num_args
207 );
208
209 let dt = args.expr_fields[0].data_type().clone();
210
211 macro_rules! helper {
212 ($t:ty, $dt:expr) => {
213 Ok(Box::new(MedianGroupsAccumulator::<$t>::new($dt)))
214 };
215 }
216
217 downcast_integer! {
218 dt => (helper, dt),
219 DataType::Float16 => helper!(Float16Type, dt),
220 DataType::Float32 => helper!(Float32Type, dt),
221 DataType::Float64 => helper!(Float64Type, dt),
222 DataType::Decimal32(_, _) => helper!(Decimal32Type, dt),
223 DataType::Decimal64(_, _) => helper!(Decimal64Type, dt),
224 DataType::Decimal128(_, _) => helper!(Decimal128Type, dt),
225 DataType::Decimal256(_, _) => helper!(Decimal256Type, dt),
226 _ => Err(DataFusionError::NotImplemented(format!(
227 "MedianGroupsAccumulator not supported for {} with {}",
228 args.name,
229 dt,
230 ))),
231 }
232 }
233
234 fn documentation(&self) -> Option<&Documentation> {
235 self.doc()
236 }
237}
238
239struct MedianAccumulator<T: ArrowNumericType> {
247 data_type: DataType,
248 all_values: Vec<T::Native>,
249}
250
251impl<T: ArrowNumericType> Debug for MedianAccumulator<T> {
252 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
253 write!(f, "MedianAccumulator({})", self.data_type)
254 }
255}
256
257impl<T: ArrowNumericType> Accumulator for MedianAccumulator<T> {
258 fn state(&mut self) -> Result<Vec<ScalarValue>> {
259 let offsets =
263 OffsetBuffer::new(ScalarBuffer::from(vec![0, self.all_values.len() as i32]));
264
265 let values_array = PrimitiveArray::<T>::new(
267 ScalarBuffer::from(std::mem::take(&mut self.all_values)),
268 None,
269 )
270 .with_data_type(self.data_type.clone());
271
272 let list_array = ListArray::new(
274 Arc::new(Field::new_list_field(self.data_type.clone(), true)),
275 offsets,
276 Arc::new(values_array),
277 None,
278 );
279
280 Ok(vec![ScalarValue::List(Arc::new(list_array))])
281 }
282
283 fn update_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
284 let values = values[0].as_primitive::<T>();
285 self.all_values.reserve(values.len() - values.null_count());
286 self.all_values.extend(values.iter().flatten());
287 Ok(())
288 }
289
290 fn merge_batch(&mut self, states: &[ArrayRef]) -> Result<()> {
291 let array = states[0].as_list::<i32>();
292 for v in array.iter().flatten() {
293 self.update_batch(&[v])?
294 }
295 Ok(())
296 }
297
298 fn evaluate(&mut self) -> Result<ScalarValue> {
299 let median = calculate_median::<T>(&mut self.all_values);
300 ScalarValue::new_primitive::<T>(median, &self.data_type)
301 }
302
303 fn size(&self) -> usize {
304 size_of_val(self) + self.all_values.capacity() * size_of::<T::Native>()
305 }
306
307 fn retract_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
308 let mut to_remove: HashMap<Hashable<T::Native>, usize> = HashMap::new();
309
310 let arr = values[0].as_primitive::<T>();
311 for value in arr.iter().flatten() {
312 *to_remove.entry(Hashable(value)).or_default() += 1;
313 }
314
315 let mut i = 0;
316 while i < self.all_values.len() {
317 let k = Hashable(self.all_values[i]);
318 if let Some(count) = to_remove.get_mut(&k)
319 && *count > 0
320 {
321 self.all_values.swap_remove(i);
322 *count -= 1;
323 if *count == 0 {
324 to_remove.remove(&k);
325 if to_remove.is_empty() {
326 break;
327 }
328 }
329 } else {
330 i += 1;
331 }
332 }
333 Ok(())
334 }
335
336 fn supports_retract_batch(&self) -> bool {
337 true
338 }
339}
340
341#[derive(Debug)]
348struct MedianGroupsAccumulator<T: ArrowNumericType + Send> {
349 data_type: DataType,
350 group_values: Vec<Vec<T::Native>>,
351}
352
353impl<T: ArrowNumericType + Send> MedianGroupsAccumulator<T> {
354 pub fn new(data_type: DataType) -> Self {
355 Self {
356 data_type,
357 group_values: Vec::new(),
358 }
359 }
360}
361
362impl<T: ArrowNumericType + Send> GroupsAccumulator for MedianGroupsAccumulator<T> {
363 fn update_batch(
364 &mut self,
365 values: &[ArrayRef],
366 group_indices: &[usize],
367 opt_filter: Option<&BooleanArray>,
368 total_num_groups: usize,
369 ) -> Result<()> {
370 assert_eq!(values.len(), 1, "single argument to update_batch");
371 let values = values[0].as_primitive::<T>();
372
373 self.group_values.resize(total_num_groups, Vec::new());
375 accumulate(
376 group_indices,
377 values,
378 opt_filter,
379 |group_index, new_value| {
380 self.group_values[group_index].push(new_value);
381 },
382 );
383
384 Ok(())
385 }
386
387 fn merge_batch(
388 &mut self,
389 values: &[ArrayRef],
390 group_indices: &[usize],
391 _opt_filter: Option<&BooleanArray>,
393 total_num_groups: usize,
394 ) -> Result<()> {
395 assert_eq!(values.len(), 1, "one argument to merge_batch");
396
397 let input_group_values = values[0].as_list::<i32>();
418
419 self.group_values.resize(total_num_groups, Vec::new());
421
422 group_indices
427 .iter()
428 .zip(input_group_values.iter())
429 .for_each(|(&group_index, values_opt)| {
430 if let Some(values) = values_opt {
431 let values = values.as_primitive::<T>();
432 self.group_values[group_index].extend(values.values().iter());
433 }
434 });
435
436 Ok(())
437 }
438
439 fn state(&mut self, emit_to: EmitTo) -> Result<Vec<ArrayRef>> {
440 let emit_group_values = emit_to.take_needed(&mut self.group_values);
442
443 let mut offsets = Vec::with_capacity(self.group_values.len() + 1);
445 offsets.push(0);
446 let mut cur_len = 0_i32;
447 for group_value in &emit_group_values {
448 cur_len += group_value.len() as i32;
449 offsets.push(cur_len);
450 }
451 let offsets = OffsetBuffer::new(ScalarBuffer::from(offsets));
459
460 let flatten_group_values =
462 emit_group_values.into_iter().flatten().collect::<Vec<_>>();
463 let group_values_array =
464 PrimitiveArray::<T>::new(ScalarBuffer::from(flatten_group_values), None)
465 .with_data_type(self.data_type.clone());
466
467 let result_list_array = ListArray::new(
469 Arc::new(Field::new_list_field(self.data_type.clone(), true)),
470 offsets,
471 Arc::new(group_values_array),
472 None,
473 );
474
475 Ok(vec![Arc::new(result_list_array)])
476 }
477
478 fn evaluate(&mut self, emit_to: EmitTo) -> Result<ArrayRef> {
479 let emit_group_values = emit_to.take_needed(&mut self.group_values);
481
482 let mut evaluate_result_builder =
484 PrimitiveBuilder::<T>::new().with_data_type(self.data_type.clone());
485 for mut values in emit_group_values {
486 let median = calculate_median::<T>(&mut values);
487 evaluate_result_builder.append_option(median);
488 }
489
490 Ok(Arc::new(evaluate_result_builder.finish()))
491 }
492
493 fn convert_to_state(
494 &self,
495 values: &[ArrayRef],
496 opt_filter: Option<&BooleanArray>,
497 ) -> Result<Vec<ArrayRef>> {
498 assert_eq!(values.len(), 1, "one argument to merge_batch");
499
500 let input_array = values[0].as_primitive::<T>();
501
502 let values = PrimitiveArray::<T>::new(input_array.values().clone(), None)
511 .with_data_type(self.data_type.clone());
512
513 let offset_end = i32::try_from(input_array.len()).map_err(|e| {
515 internal_datafusion_err!(
516 "cast array_len to i32 failed in convert_to_state of group median, err:{e:?}"
517 )
518 })?;
519 let offsets = (0..=offset_end).collect::<Vec<_>>();
520 let offsets = unsafe { OffsetBuffer::new_unchecked(ScalarBuffer::from(offsets)) };
522
523 let nulls = filtered_null_mask(opt_filter, input_array);
525
526 let converted_list_array = ListArray::new(
527 Arc::new(Field::new_list_field(self.data_type.clone(), true)),
528 offsets,
529 Arc::new(values),
530 nulls,
531 );
532
533 Ok(vec![Arc::new(converted_list_array)])
534 }
535
536 fn supports_convert_to_state(&self) -> bool {
537 true
538 }
539
540 fn size(&self) -> usize {
541 self.group_values
542 .iter()
543 .map(|values| values.capacity() * size_of::<T>())
544 .sum::<usize>()
545 + self.group_values.capacity() * size_of::<Vec<T>>()
547 }
548}
549
550#[derive(Debug)]
551struct DistinctMedianAccumulator<T: ArrowNumericType> {
552 distinct_values: GenericDistinctBuffer<T>,
553 data_type: DataType,
554}
555
556impl<T: ArrowNumericType + Debug> Accumulator for DistinctMedianAccumulator<T> {
557 fn state(&mut self) -> Result<Vec<ScalarValue>> {
558 self.distinct_values.state()
559 }
560
561 fn update_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
562 self.distinct_values.update_batch(values)
563 }
564
565 fn merge_batch(&mut self, states: &[ArrayRef]) -> Result<()> {
566 self.distinct_values.merge_batch(states)
567 }
568
569 fn evaluate(&mut self) -> Result<ScalarValue> {
570 let mut d: Vec<T::Native> =
571 self.distinct_values.values.iter().map(|v| v.0).collect();
572 let median = calculate_median::<T>(&mut d);
573 ScalarValue::new_primitive::<T>(median, &self.data_type)
574 }
575
576 fn size(&self) -> usize {
577 size_of_val(self) + self.distinct_values.size()
578 }
579}
580
581fn slice_max<T>(array: &[T::Native]) -> T::Native
583where
584 T: ArrowPrimitiveType,
585 T::Native: PartialOrd, {
587 debug_assert!(!array.is_empty());
589 *array
591 .iter()
592 .max_by(|x, y| x.partial_cmp(y).unwrap_or(Ordering::Less))
593 .unwrap()
594}
595
596fn calculate_median<T: ArrowNumericType>(values: &mut [T::Native]) -> Option<T::Native> {
597 let cmp = |x: &T::Native, y: &T::Native| x.compare(*y);
598
599 let len = values.len();
600 if len == 0 {
601 None
602 } else if len % 2 == 0 {
603 let (low, high, _) = values.select_nth_unstable_by(len / 2, cmp);
604 let left_max = slice_max::<T>(low);
606 let two = T::Native::usize_as(2);
609 let median = match left_max.add_checked(*high) {
610 Ok(sum) => sum.div_wrapping(two),
611 Err(_) => {
612 let half_left = left_max.div_wrapping(two);
616 let half_right = (*high).div_wrapping(two);
617 let rem_left = left_max.mod_wrapping(two);
618 let rem_right = (*high).mod_wrapping(two);
619 let correction = rem_left.add_wrapping(rem_right).div_wrapping(two);
622 half_left.add_wrapping(half_right).add_wrapping(correction)
623 }
624 };
625 Some(median)
626 } else {
627 let (_, median, _) = values.select_nth_unstable_by(len / 2, cmp);
628 Some(*median)
629 }
630}