datafusion_functions_aggregate/
approx_median.rs1use arrow::datatypes::DataType::{Float64, UInt64};
21use arrow::datatypes::{DataType, Field, FieldRef};
22use datafusion_common::types::NativeType;
23use datafusion_functions_aggregate_common::noop_accumulator::NoopAccumulator;
24use std::fmt::Debug;
25use std::sync::Arc;
26
27use datafusion_common::{Result, not_impl_err};
28use datafusion_expr::function::{AccumulatorArgs, StateFieldsArgs};
29use datafusion_expr::utils::format_state_name;
30use datafusion_expr::{
31 Accumulator, AggregateUDFImpl, Coercion, Documentation, Signature, TypeSignature,
32 TypeSignatureClass, Volatility,
33};
34use datafusion_macros::user_doc;
35
36use crate::approx_percentile_cont::ApproxPercentileAccumulator;
37
38make_udaf_expr_and_func!(
39 ApproxMedian,
40 approx_median,
41 expression,
42 "Computes the approximate median of a set of numbers",
43 approx_median_udaf
44);
45
46#[user_doc(
48 doc_section(label = "Approximate Functions"),
49 description = "Returns the approximate median (50th percentile) of input values. It is an alias of `approx_percentile_cont(0.5) WITHIN GROUP (ORDER BY x)`.",
50 syntax_example = "approx_median(expression)",
51 sql_example = r#"```sql
52> SELECT approx_median(column_name) FROM table_name;
53+-----------------------------------+
54| approx_median(column_name) |
55+-----------------------------------+
56| 23.5 |
57+-----------------------------------+
58```"#,
59 standard_argument(name = "expression",)
60)]
61#[derive(Debug, PartialEq, Eq, Hash)]
62pub struct ApproxMedian {
63 signature: Signature,
64}
65
66impl Default for ApproxMedian {
67 fn default() -> Self {
68 Self::new()
69 }
70}
71
72impl ApproxMedian {
73 pub fn new() -> Self {
75 Self {
76 signature: Signature::one_of(
77 vec![TypeSignature::Coercible(vec![Coercion::new_implicit(
78 TypeSignatureClass::Float,
79 vec![TypeSignatureClass::Numeric],
80 NativeType::Float64,
81 )])],
82 Volatility::Immutable,
83 ),
84 }
85 }
86}
87
88impl AggregateUDFImpl for ApproxMedian {
89 fn state_fields(&self, args: StateFieldsArgs) -> Result<Vec<FieldRef>> {
90 if args.input_fields[0].data_type().is_null() {
91 Ok(vec![
92 Field::new(
93 format_state_name(args.name, self.name()),
94 DataType::Null,
95 true,
96 )
97 .into(),
98 ])
99 } else {
100 Ok(vec![
101 Field::new(format_state_name(args.name, "max_size"), UInt64, false),
102 Field::new(format_state_name(args.name, "sum"), Float64, false),
103 Field::new(format_state_name(args.name, "count"), Float64, false),
104 Field::new(format_state_name(args.name, "max"), Float64, false),
105 Field::new(format_state_name(args.name, "min"), Float64, false),
106 Field::new_list(
107 format_state_name(args.name, "centroids"),
108 Field::new_list_field(Float64, true),
109 false,
110 ),
111 ]
112 .into_iter()
113 .map(Arc::new)
114 .collect())
115 }
116 }
117
118 fn name(&self) -> &str {
119 "approx_median"
120 }
121
122 fn signature(&self) -> &Signature {
123 &self.signature
124 }
125
126 fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
127 Ok(arg_types[0].clone())
128 }
129
130 fn accumulator(&self, acc_args: AccumulatorArgs) -> Result<Box<dyn Accumulator>> {
131 if acc_args.is_distinct {
132 return not_impl_err!(
133 "APPROX_MEDIAN(DISTINCT) aggregations are not available"
134 );
135 }
136
137 if acc_args.expr_fields[0].data_type().is_null() {
138 Ok(Box::new(NoopAccumulator::default()))
139 } else {
140 Ok(Box::new(ApproxPercentileAccumulator::new(
141 0.5_f64,
142 acc_args.expr_fields[0].data_type().clone(),
143 )))
144 }
145 }
146
147 fn documentation(&self) -> Option<&Documentation> {
148 self.doc()
149 }
150}