polars_plan/dsl/function_expr/
mod.rs

1#[cfg(feature = "abs")]
2mod abs;
3#[cfg(feature = "arg_where")]
4mod arg_where;
5#[cfg(feature = "dtype-array")]
6mod array;
7mod binary;
8#[cfg(feature = "bitwise")]
9mod bitwise;
10mod boolean;
11mod bounds;
12#[cfg(feature = "business")]
13mod business;
14#[cfg(feature = "dtype-categorical")]
15pub mod cat;
16#[cfg(feature = "round_series")]
17mod clip;
18#[cfg(feature = "dtype-struct")]
19mod coerce;
20mod concat;
21#[cfg(feature = "cov")]
22mod correlation;
23#[cfg(feature = "cum_agg")]
24mod cum;
25#[cfg(feature = "cutqcut")]
26mod cut;
27#[cfg(feature = "temporal")]
28mod datetime;
29mod dispatch;
30#[cfg(feature = "ewma")]
31mod ewm;
32#[cfg(feature = "ewma_by")]
33mod ewm_by;
34mod fill_null;
35#[cfg(feature = "fused")]
36mod fused;
37#[cfg(feature = "index_of")]
38mod index_of;
39mod list;
40#[cfg(feature = "log")]
41mod log;
42mod nan;
43#[cfg(feature = "peaks")]
44mod peaks;
45#[cfg(feature = "ffi_plugin")]
46mod plugin;
47pub mod pow;
48#[cfg(feature = "random")]
49mod random;
50#[cfg(feature = "range")]
51mod range;
52mod repeat;
53#[cfg(feature = "rolling_window")]
54pub mod rolling;
55#[cfg(feature = "rolling_window_by")]
56pub mod rolling_by;
57#[cfg(feature = "round_series")]
58mod round;
59#[cfg(feature = "row_hash")]
60mod row_hash;
61pub(super) mod schema;
62#[cfg(feature = "search_sorted")]
63mod search_sorted;
64mod shift_and_fill;
65mod shrink_type;
66#[cfg(feature = "sign")]
67mod sign;
68#[cfg(feature = "strings")]
69mod strings;
70#[cfg(feature = "dtype-struct")]
71mod struct_;
72#[cfg(feature = "temporal")]
73mod temporal;
74#[cfg(feature = "trigonometry")]
75pub mod trigonometry;
76mod unique;
77
78use std::fmt::{Display, Formatter};
79use std::hash::{Hash, Hasher};
80
81#[cfg(feature = "dtype-array")]
82pub use array::ArrayFunction;
83#[cfg(feature = "cov")]
84pub use correlation::CorrelationMethod;
85#[cfg(feature = "fused")]
86pub use fused::FusedOperator;
87pub use list::ListFunction;
88pub use polars_core::datatypes::ReshapeDimension;
89use polars_core::prelude::*;
90#[cfg(feature = "random")]
91pub use random::RandomMethod;
92use schema::FieldsMapper;
93#[cfg(feature = "serde")]
94use serde::{Deserialize, Serialize};
95
96pub use self::binary::BinaryFunction;
97#[cfg(feature = "bitwise")]
98pub use self::bitwise::BitwiseFunction;
99pub use self::boolean::BooleanFunction;
100#[cfg(feature = "business")]
101pub use self::business::BusinessFunction;
102#[cfg(feature = "dtype-categorical")]
103pub use self::cat::CategoricalFunction;
104#[cfg(feature = "temporal")]
105pub use self::datetime::TemporalFunction;
106pub use self::pow::PowFunction;
107#[cfg(feature = "range")]
108pub(super) use self::range::RangeFunction;
109#[cfg(feature = "rolling_window")]
110pub(super) use self::rolling::RollingFunction;
111#[cfg(feature = "rolling_window_by")]
112pub(super) use self::rolling_by::RollingFunctionBy;
113#[cfg(feature = "strings")]
114pub use self::strings::StringFunction;
115#[cfg(feature = "dtype-struct")]
116pub use self::struct_::StructFunction;
117#[cfg(feature = "trigonometry")]
118pub use self::trigonometry::TrigonometricFunction;
119use super::*;
120
121#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
122#[derive(Clone, PartialEq, Debug)]
123pub enum FunctionExpr {
124    // Namespaces
125    #[cfg(feature = "dtype-array")]
126    ArrayExpr(ArrayFunction),
127    BinaryExpr(BinaryFunction),
128    #[cfg(feature = "dtype-categorical")]
129    Categorical(CategoricalFunction),
130    ListExpr(ListFunction),
131    #[cfg(feature = "strings")]
132    StringExpr(StringFunction),
133    #[cfg(feature = "dtype-struct")]
134    StructExpr(StructFunction),
135    #[cfg(feature = "temporal")]
136    TemporalExpr(TemporalFunction),
137    #[cfg(feature = "bitwise")]
138    Bitwise(BitwiseFunction),
139
140    // Other expressions
141    Boolean(BooleanFunction),
142    #[cfg(feature = "business")]
143    Business(BusinessFunction),
144    #[cfg(feature = "abs")]
145    Abs,
146    Negate,
147    #[cfg(feature = "hist")]
148    Hist {
149        bin_count: Option<usize>,
150        include_category: bool,
151        include_breakpoint: bool,
152    },
153    NullCount,
154    Pow(PowFunction),
155    #[cfg(feature = "row_hash")]
156    Hash(u64, u64, u64, u64),
157    #[cfg(feature = "arg_where")]
158    ArgWhere,
159    #[cfg(feature = "index_of")]
160    IndexOf,
161    #[cfg(feature = "search_sorted")]
162    SearchSorted(SearchSortedSide),
163    #[cfg(feature = "range")]
164    Range(RangeFunction),
165    #[cfg(feature = "trigonometry")]
166    Trigonometry(TrigonometricFunction),
167    #[cfg(feature = "trigonometry")]
168    Atan2,
169    #[cfg(feature = "sign")]
170    Sign,
171    FillNull,
172    FillNullWithStrategy(FillNullStrategy),
173    #[cfg(feature = "rolling_window")]
174    RollingExpr(RollingFunction),
175    #[cfg(feature = "rolling_window_by")]
176    RollingExprBy(RollingFunctionBy),
177    ShiftAndFill,
178    Shift,
179    DropNans,
180    DropNulls,
181    #[cfg(feature = "mode")]
182    Mode,
183    #[cfg(feature = "moment")]
184    Skew(bool),
185    #[cfg(feature = "moment")]
186    Kurtosis(bool, bool),
187    #[cfg(feature = "dtype-array")]
188    Reshape(Vec<ReshapeDimension>),
189    #[cfg(feature = "repeat_by")]
190    RepeatBy,
191    ArgUnique,
192    #[cfg(feature = "rank")]
193    Rank {
194        options: RankOptions,
195        seed: Option<u64>,
196    },
197    Repeat,
198    #[cfg(feature = "round_series")]
199    Clip {
200        has_min: bool,
201        has_max: bool,
202    },
203    #[cfg(feature = "dtype-struct")]
204    AsStruct,
205    #[cfg(feature = "top_k")]
206    TopK {
207        descending: bool,
208    },
209    #[cfg(feature = "top_k")]
210    TopKBy {
211        descending: Vec<bool>,
212    },
213    #[cfg(feature = "cum_agg")]
214    CumCount {
215        reverse: bool,
216    },
217    #[cfg(feature = "cum_agg")]
218    CumSum {
219        reverse: bool,
220    },
221    #[cfg(feature = "cum_agg")]
222    CumProd {
223        reverse: bool,
224    },
225    #[cfg(feature = "cum_agg")]
226    CumMin {
227        reverse: bool,
228    },
229    #[cfg(feature = "cum_agg")]
230    CumMax {
231        reverse: bool,
232    },
233    Reverse,
234    #[cfg(feature = "dtype-struct")]
235    ValueCounts {
236        sort: bool,
237        parallel: bool,
238        name: PlSmallStr,
239        normalize: bool,
240    },
241    #[cfg(feature = "unique_counts")]
242    UniqueCounts,
243    #[cfg(feature = "approx_unique")]
244    ApproxNUnique,
245    Coalesce,
246    ShrinkType,
247    #[cfg(feature = "diff")]
248    Diff(NullBehavior),
249    #[cfg(feature = "pct_change")]
250    PctChange,
251    #[cfg(feature = "interpolate")]
252    Interpolate(InterpolationMethod),
253    #[cfg(feature = "interpolate_by")]
254    InterpolateBy,
255    #[cfg(feature = "log")]
256    Entropy {
257        base: f64,
258        normalize: bool,
259    },
260    #[cfg(feature = "log")]
261    Log {
262        base: f64,
263    },
264    #[cfg(feature = "log")]
265    Log1p,
266    #[cfg(feature = "log")]
267    Exp,
268    Unique(bool),
269    #[cfg(feature = "round_series")]
270    Round {
271        decimals: u32,
272        mode: RoundMode,
273    },
274    #[cfg(feature = "round_series")]
275    RoundSF {
276        digits: i32,
277    },
278    #[cfg(feature = "round_series")]
279    Floor,
280    #[cfg(feature = "round_series")]
281    Ceil,
282    UpperBound,
283    LowerBound,
284    #[cfg(feature = "fused")]
285    Fused(fused::FusedOperator),
286    ConcatExpr(bool),
287    #[cfg(feature = "cov")]
288    Correlation {
289        method: correlation::CorrelationMethod,
290    },
291    #[cfg(feature = "peaks")]
292    PeakMin,
293    #[cfg(feature = "peaks")]
294    PeakMax,
295    #[cfg(feature = "cutqcut")]
296    Cut {
297        breaks: Vec<f64>,
298        labels: Option<Vec<PlSmallStr>>,
299        left_closed: bool,
300        include_breaks: bool,
301    },
302    #[cfg(feature = "cutqcut")]
303    QCut {
304        probs: Vec<f64>,
305        labels: Option<Vec<PlSmallStr>>,
306        left_closed: bool,
307        allow_duplicates: bool,
308        include_breaks: bool,
309    },
310    #[cfg(feature = "rle")]
311    RLE,
312    #[cfg(feature = "rle")]
313    RLEID,
314    ToPhysical,
315    #[cfg(feature = "random")]
316    Random {
317        method: random::RandomMethod,
318        seed: Option<u64>,
319    },
320    SetSortedFlag(IsSorted),
321    #[cfg(feature = "ffi_plugin")]
322    /// Creating this node is unsafe
323    /// This will lead to calls over FFI.
324    FfiPlugin {
325        flags: FunctionOptions,
326        /// Shared library.
327        lib: PlSmallStr,
328        /// Identifier in the shared lib.
329        symbol: PlSmallStr,
330        /// Pickle serialized keyword arguments.
331        kwargs: Arc<[u8]>,
332    },
333    MaxHorizontal,
334    MinHorizontal,
335    SumHorizontal {
336        ignore_nulls: bool,
337    },
338    MeanHorizontal {
339        ignore_nulls: bool,
340    },
341    #[cfg(feature = "ewma")]
342    EwmMean {
343        options: EWMOptions,
344    },
345    #[cfg(feature = "ewma_by")]
346    EwmMeanBy {
347        half_life: Duration,
348    },
349    #[cfg(feature = "ewma")]
350    EwmStd {
351        options: EWMOptions,
352    },
353    #[cfg(feature = "ewma")]
354    EwmVar {
355        options: EWMOptions,
356    },
357    #[cfg(feature = "replace")]
358    Replace,
359    #[cfg(feature = "replace")]
360    ReplaceStrict {
361        return_dtype: Option<DataType>,
362    },
363    GatherEvery {
364        n: usize,
365        offset: usize,
366    },
367    #[cfg(feature = "reinterpret")]
368    Reinterpret(bool),
369    ExtendConstant,
370}
371
372impl Hash for FunctionExpr {
373    fn hash<H: Hasher>(&self, state: &mut H) {
374        std::mem::discriminant(self).hash(state);
375        use FunctionExpr::*;
376        match self {
377            // Namespaces
378            #[cfg(feature = "dtype-array")]
379            ArrayExpr(f) => f.hash(state),
380            BinaryExpr(f) => f.hash(state),
381            #[cfg(feature = "dtype-categorical")]
382            Categorical(f) => f.hash(state),
383            ListExpr(f) => f.hash(state),
384            #[cfg(feature = "strings")]
385            StringExpr(f) => f.hash(state),
386            #[cfg(feature = "dtype-struct")]
387            StructExpr(f) => f.hash(state),
388            #[cfg(feature = "temporal")]
389            TemporalExpr(f) => f.hash(state),
390            #[cfg(feature = "bitwise")]
391            Bitwise(f) => f.hash(state),
392
393            // Other expressions
394            Boolean(f) => f.hash(state),
395            #[cfg(feature = "business")]
396            Business(f) => f.hash(state),
397            Pow(f) => f.hash(state),
398            #[cfg(feature = "index_of")]
399            IndexOf => {},
400            #[cfg(feature = "search_sorted")]
401            SearchSorted(f) => f.hash(state),
402            #[cfg(feature = "random")]
403            Random { method, .. } => method.hash(state),
404            #[cfg(feature = "cov")]
405            Correlation { method, .. } => method.hash(state),
406            #[cfg(feature = "range")]
407            Range(f) => f.hash(state),
408            #[cfg(feature = "trigonometry")]
409            Trigonometry(f) => f.hash(state),
410            #[cfg(feature = "fused")]
411            Fused(f) => f.hash(state),
412            #[cfg(feature = "diff")]
413            Diff(null_behavior) => null_behavior.hash(state),
414            #[cfg(feature = "interpolate")]
415            Interpolate(f) => f.hash(state),
416            #[cfg(feature = "interpolate_by")]
417            InterpolateBy => {},
418            #[cfg(feature = "ffi_plugin")]
419            FfiPlugin {
420                flags: _,
421                lib,
422                symbol,
423                kwargs,
424            } => {
425                kwargs.hash(state);
426                lib.hash(state);
427                symbol.hash(state);
428            },
429            MaxHorizontal
430            | MinHorizontal
431            | SumHorizontal { .. }
432            | MeanHorizontal { .. }
433            | DropNans
434            | DropNulls
435            | Reverse
436            | ArgUnique
437            | Shift
438            | ShiftAndFill => {},
439            #[cfg(feature = "mode")]
440            Mode => {},
441            #[cfg(feature = "abs")]
442            Abs => {},
443            Negate => {},
444            NullCount => {},
445            #[cfg(feature = "arg_where")]
446            ArgWhere => {},
447            #[cfg(feature = "trigonometry")]
448            Atan2 => {},
449            #[cfg(feature = "dtype-struct")]
450            AsStruct => {},
451            #[cfg(feature = "sign")]
452            Sign => {},
453            #[cfg(feature = "row_hash")]
454            Hash(a, b, c, d) => (a, b, c, d).hash(state),
455            FillNull => {},
456            #[cfg(feature = "rolling_window")]
457            RollingExpr(f) => {
458                f.hash(state);
459            },
460            #[cfg(feature = "rolling_window_by")]
461            RollingExprBy(f) => {
462                f.hash(state);
463            },
464            #[cfg(feature = "moment")]
465            Skew(a) => a.hash(state),
466            #[cfg(feature = "moment")]
467            Kurtosis(a, b) => {
468                a.hash(state);
469                b.hash(state);
470            },
471            Repeat => {},
472            #[cfg(feature = "rank")]
473            Rank { options, seed } => {
474                options.hash(state);
475                seed.hash(state);
476            },
477            #[cfg(feature = "round_series")]
478            Clip { has_min, has_max } => {
479                has_min.hash(state);
480                has_max.hash(state);
481            },
482            #[cfg(feature = "top_k")]
483            TopK { descending } => descending.hash(state),
484            #[cfg(feature = "cum_agg")]
485            CumCount { reverse } => reverse.hash(state),
486            #[cfg(feature = "cum_agg")]
487            CumSum { reverse } => reverse.hash(state),
488            #[cfg(feature = "cum_agg")]
489            CumProd { reverse } => reverse.hash(state),
490            #[cfg(feature = "cum_agg")]
491            CumMin { reverse } => reverse.hash(state),
492            #[cfg(feature = "cum_agg")]
493            CumMax { reverse } => reverse.hash(state),
494            #[cfg(feature = "dtype-struct")]
495            ValueCounts {
496                sort,
497                parallel,
498                name,
499                normalize,
500            } => {
501                sort.hash(state);
502                parallel.hash(state);
503                name.hash(state);
504                normalize.hash(state);
505            },
506            #[cfg(feature = "unique_counts")]
507            UniqueCounts => {},
508            #[cfg(feature = "approx_unique")]
509            ApproxNUnique => {},
510            Coalesce => {},
511            ShrinkType => {},
512            #[cfg(feature = "pct_change")]
513            PctChange => {},
514            #[cfg(feature = "log")]
515            Entropy { base, normalize } => {
516                base.to_bits().hash(state);
517                normalize.hash(state);
518            },
519            #[cfg(feature = "log")]
520            Log { base } => base.to_bits().hash(state),
521            #[cfg(feature = "log")]
522            Log1p => {},
523            #[cfg(feature = "log")]
524            Exp => {},
525            Unique(a) => a.hash(state),
526            #[cfg(feature = "round_series")]
527            Round { decimals, mode } => {
528                decimals.hash(state);
529                mode.hash(state);
530            },
531            #[cfg(feature = "round_series")]
532            FunctionExpr::RoundSF { digits } => digits.hash(state),
533            #[cfg(feature = "round_series")]
534            FunctionExpr::Floor => {},
535            #[cfg(feature = "round_series")]
536            Ceil => {},
537            UpperBound => {},
538            LowerBound => {},
539            ConcatExpr(a) => a.hash(state),
540            #[cfg(feature = "peaks")]
541            PeakMin => {},
542            #[cfg(feature = "peaks")]
543            PeakMax => {},
544            #[cfg(feature = "cutqcut")]
545            Cut {
546                breaks,
547                labels,
548                left_closed,
549                include_breaks,
550            } => {
551                let slice = bytemuck::cast_slice::<_, u64>(breaks);
552                slice.hash(state);
553                labels.hash(state);
554                left_closed.hash(state);
555                include_breaks.hash(state);
556            },
557            #[cfg(feature = "dtype-array")]
558            Reshape(dims) => dims.hash(state),
559            #[cfg(feature = "repeat_by")]
560            RepeatBy => {},
561            #[cfg(feature = "cutqcut")]
562            QCut {
563                probs,
564                labels,
565                left_closed,
566                allow_duplicates,
567                include_breaks,
568            } => {
569                let slice = bytemuck::cast_slice::<_, u64>(probs);
570                slice.hash(state);
571                labels.hash(state);
572                left_closed.hash(state);
573                allow_duplicates.hash(state);
574                include_breaks.hash(state);
575            },
576            #[cfg(feature = "rle")]
577            RLE => {},
578            #[cfg(feature = "rle")]
579            RLEID => {},
580            ToPhysical => {},
581            SetSortedFlag(is_sorted) => is_sorted.hash(state),
582            #[cfg(feature = "ewma")]
583            EwmMean { options } => options.hash(state),
584            #[cfg(feature = "ewma_by")]
585            EwmMeanBy { half_life } => (half_life).hash(state),
586            #[cfg(feature = "ewma")]
587            EwmStd { options } => options.hash(state),
588            #[cfg(feature = "ewma")]
589            EwmVar { options } => options.hash(state),
590            #[cfg(feature = "hist")]
591            Hist {
592                bin_count,
593                include_category,
594                include_breakpoint,
595            } => {
596                bin_count.hash(state);
597                include_category.hash(state);
598                include_breakpoint.hash(state);
599            },
600            #[cfg(feature = "replace")]
601            Replace => {},
602            #[cfg(feature = "replace")]
603            ReplaceStrict { return_dtype } => return_dtype.hash(state),
604            FillNullWithStrategy(strategy) => strategy.hash(state),
605            GatherEvery { n, offset } => (n, offset).hash(state),
606            #[cfg(feature = "reinterpret")]
607            Reinterpret(signed) => signed.hash(state),
608            ExtendConstant => {},
609            #[cfg(feature = "top_k")]
610            TopKBy { descending } => descending.hash(state),
611        }
612    }
613}
614
615impl Display for FunctionExpr {
616    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
617        use FunctionExpr::*;
618        let s = match self {
619            // Namespaces
620            #[cfg(feature = "dtype-array")]
621            ArrayExpr(func) => return write!(f, "{func}"),
622            BinaryExpr(func) => return write!(f, "{func}"),
623            #[cfg(feature = "dtype-categorical")]
624            Categorical(func) => return write!(f, "{func}"),
625            ListExpr(func) => return write!(f, "{func}"),
626            #[cfg(feature = "strings")]
627            StringExpr(func) => return write!(f, "{func}"),
628            #[cfg(feature = "dtype-struct")]
629            StructExpr(func) => return write!(f, "{func}"),
630            #[cfg(feature = "temporal")]
631            TemporalExpr(func) => return write!(f, "{func}"),
632            #[cfg(feature = "bitwise")]
633            Bitwise(func) => return write!(f, "bitwise_{func}"),
634
635            // Other expressions
636            Boolean(func) => return write!(f, "{func}"),
637            #[cfg(feature = "business")]
638            Business(func) => return write!(f, "{func}"),
639            #[cfg(feature = "abs")]
640            Abs => "abs",
641            Negate => "negate",
642            NullCount => "null_count",
643            Pow(func) => return write!(f, "{func}"),
644            #[cfg(feature = "row_hash")]
645            Hash(_, _, _, _) => "hash",
646            #[cfg(feature = "arg_where")]
647            ArgWhere => "arg_where",
648            #[cfg(feature = "index_of")]
649            IndexOf => "index_of",
650            #[cfg(feature = "search_sorted")]
651            SearchSorted(_) => "search_sorted",
652            #[cfg(feature = "range")]
653            Range(func) => return write!(f, "{func}"),
654            #[cfg(feature = "trigonometry")]
655            Trigonometry(func) => return write!(f, "{func}"),
656            #[cfg(feature = "trigonometry")]
657            Atan2 => return write!(f, "arctan2"),
658            #[cfg(feature = "sign")]
659            Sign => "sign",
660            FillNull => "fill_null",
661            #[cfg(feature = "rolling_window")]
662            RollingExpr(func, ..) => return write!(f, "{func}"),
663            #[cfg(feature = "rolling_window_by")]
664            RollingExprBy(func, ..) => return write!(f, "{func}"),
665            ShiftAndFill => "shift_and_fill",
666            DropNans => "drop_nans",
667            DropNulls => "drop_nulls",
668            #[cfg(feature = "mode")]
669            Mode => "mode",
670            #[cfg(feature = "moment")]
671            Skew(_) => "skew",
672            #[cfg(feature = "moment")]
673            Kurtosis(..) => "kurtosis",
674            ArgUnique => "arg_unique",
675            Repeat => "repeat",
676            #[cfg(feature = "rank")]
677            Rank { .. } => "rank",
678            #[cfg(feature = "round_series")]
679            Clip { has_min, has_max } => match (has_min, has_max) {
680                (true, true) => "clip",
681                (false, true) => "clip_max",
682                (true, false) => "clip_min",
683                _ => unreachable!(),
684            },
685            #[cfg(feature = "dtype-struct")]
686            AsStruct => "as_struct",
687            #[cfg(feature = "top_k")]
688            TopK { descending } => {
689                if *descending {
690                    "bottom_k"
691                } else {
692                    "top_k"
693                }
694            },
695            #[cfg(feature = "top_k")]
696            TopKBy { .. } => "top_k_by",
697            Shift => "shift",
698            #[cfg(feature = "cum_agg")]
699            CumCount { .. } => "cum_count",
700            #[cfg(feature = "cum_agg")]
701            CumSum { .. } => "cum_sum",
702            #[cfg(feature = "cum_agg")]
703            CumProd { .. } => "cum_prod",
704            #[cfg(feature = "cum_agg")]
705            CumMin { .. } => "cum_min",
706            #[cfg(feature = "cum_agg")]
707            CumMax { .. } => "cum_max",
708            #[cfg(feature = "dtype-struct")]
709            ValueCounts { .. } => "value_counts",
710            #[cfg(feature = "unique_counts")]
711            UniqueCounts => "unique_counts",
712            Reverse => "reverse",
713            #[cfg(feature = "approx_unique")]
714            ApproxNUnique => "approx_n_unique",
715            Coalesce => "coalesce",
716            ShrinkType => "shrink_dtype",
717            #[cfg(feature = "diff")]
718            Diff(_) => "diff",
719            #[cfg(feature = "pct_change")]
720            PctChange => "pct_change",
721            #[cfg(feature = "interpolate")]
722            Interpolate(_) => "interpolate",
723            #[cfg(feature = "interpolate_by")]
724            InterpolateBy => "interpolate_by",
725            #[cfg(feature = "log")]
726            Entropy { .. } => "entropy",
727            #[cfg(feature = "log")]
728            Log { .. } => "log",
729            #[cfg(feature = "log")]
730            Log1p => "log1p",
731            #[cfg(feature = "log")]
732            Exp => "exp",
733            Unique(stable) => {
734                if *stable {
735                    "unique_stable"
736                } else {
737                    "unique"
738                }
739            },
740            #[cfg(feature = "round_series")]
741            Round { .. } => "round",
742            #[cfg(feature = "round_series")]
743            RoundSF { .. } => "round_sig_figs",
744            #[cfg(feature = "round_series")]
745            Floor => "floor",
746            #[cfg(feature = "round_series")]
747            Ceil => "ceil",
748            UpperBound => "upper_bound",
749            LowerBound => "lower_bound",
750            #[cfg(feature = "fused")]
751            Fused(fused) => return Display::fmt(fused, f),
752            ConcatExpr(_) => "concat_expr",
753            #[cfg(feature = "cov")]
754            Correlation { method, .. } => return Display::fmt(method, f),
755            #[cfg(feature = "peaks")]
756            PeakMin => "peak_min",
757            #[cfg(feature = "peaks")]
758            PeakMax => "peak_max",
759            #[cfg(feature = "cutqcut")]
760            Cut { .. } => "cut",
761            #[cfg(feature = "cutqcut")]
762            QCut { .. } => "qcut",
763            #[cfg(feature = "dtype-array")]
764            Reshape(_) => "reshape",
765            #[cfg(feature = "repeat_by")]
766            RepeatBy => "repeat_by",
767            #[cfg(feature = "rle")]
768            RLE => "rle",
769            #[cfg(feature = "rle")]
770            RLEID => "rle_id",
771            ToPhysical => "to_physical",
772            #[cfg(feature = "random")]
773            Random { method, .. } => method.into(),
774            SetSortedFlag(_) => "set_sorted",
775            #[cfg(feature = "ffi_plugin")]
776            FfiPlugin { lib, symbol, .. } => return write!(f, "{lib}:{symbol}"),
777            MaxHorizontal => "max_horizontal",
778            MinHorizontal => "min_horizontal",
779            SumHorizontal { .. } => "sum_horizontal",
780            MeanHorizontal { .. } => "mean_horizontal",
781            #[cfg(feature = "ewma")]
782            EwmMean { .. } => "ewm_mean",
783            #[cfg(feature = "ewma_by")]
784            EwmMeanBy { .. } => "ewm_mean_by",
785            #[cfg(feature = "ewma")]
786            EwmStd { .. } => "ewm_std",
787            #[cfg(feature = "ewma")]
788            EwmVar { .. } => "ewm_var",
789            #[cfg(feature = "hist")]
790            Hist { .. } => "hist",
791            #[cfg(feature = "replace")]
792            Replace => "replace",
793            #[cfg(feature = "replace")]
794            ReplaceStrict { .. } => "replace_strict",
795            FillNullWithStrategy(_) => "fill_null_with_strategy",
796            GatherEvery { .. } => "gather_every",
797            #[cfg(feature = "reinterpret")]
798            Reinterpret(_) => "reinterpret",
799            ExtendConstant => "extend_constant",
800        };
801        write!(f, "{s}")
802    }
803}
804
805#[macro_export]
806macro_rules! wrap {
807    ($e:expr) => {
808        SpecialEq::new(Arc::new($e))
809    };
810
811    ($e:expr, $($args:expr),*) => {{
812        let f = move |s: &mut [Column]| {
813            $e(s, $($args),*)
814        };
815
816        SpecialEq::new(Arc::new(f))
817    }};
818}
819
820/// `Fn(&[Column], args)`
821/// * all expression arguments are in the slice.
822/// * the first element is the root expression.
823#[macro_export]
824macro_rules! map_as_slice {
825    ($func:path) => {{
826        let f = move |s: &mut [Column]| {
827            $func(s).map(Some)
828        };
829
830        SpecialEq::new(Arc::new(f))
831    }};
832
833    ($func:path, $($args:expr),*) => {{
834        let f = move |s: &mut [Column]| {
835            $func(s, $($args),*).map(Some)
836        };
837
838        SpecialEq::new(Arc::new(f))
839    }};
840}
841
842/// * `FnOnce(Series)`
843/// * `FnOnce(Series, args)`
844#[macro_export]
845macro_rules! map_owned {
846    ($func:path) => {{
847        let f = move |c: &mut [Column]| {
848            let c = std::mem::take(&mut c[0]);
849            $func(c).map(Some)
850        };
851
852        SpecialEq::new(Arc::new(f))
853    }};
854
855    ($func:path, $($args:expr),*) => {{
856        let f = move |c: &mut [Column]| {
857            let c = std::mem::take(&mut c[0]);
858            $func(c, $($args),*).map(Some)
859        };
860
861        SpecialEq::new(Arc::new(f))
862    }};
863}
864
865/// `Fn(&Series, args)`
866#[macro_export]
867macro_rules! map {
868    ($func:path) => {{
869        let f = move |c: &mut [Column]| {
870            let c = &c[0];
871            $func(c).map(Some)
872        };
873
874        SpecialEq::new(Arc::new(f))
875    }};
876
877    ($func:path, $($args:expr),*) => {{
878        let f = move |c: &mut [Column]| {
879            let c = &c[0];
880            $func(c, $($args),*).map(Some)
881        };
882
883        SpecialEq::new(Arc::new(f))
884    }};
885}
886
887impl From<FunctionExpr> for SpecialEq<Arc<dyn ColumnsUdf>> {
888    fn from(func: FunctionExpr) -> Self {
889        use FunctionExpr::*;
890        match func {
891            // Namespaces
892            #[cfg(feature = "dtype-array")]
893            ArrayExpr(func) => func.into(),
894            BinaryExpr(func) => func.into(),
895            #[cfg(feature = "dtype-categorical")]
896            Categorical(func) => func.into(),
897            ListExpr(func) => func.into(),
898            #[cfg(feature = "strings")]
899            StringExpr(func) => func.into(),
900            #[cfg(feature = "dtype-struct")]
901            StructExpr(func) => func.into(),
902            #[cfg(feature = "temporal")]
903            TemporalExpr(func) => func.into(),
904            #[cfg(feature = "bitwise")]
905            Bitwise(func) => func.into(),
906
907            // Other expressions
908            Boolean(func) => func.into(),
909            #[cfg(feature = "business")]
910            Business(func) => func.into(),
911            #[cfg(feature = "abs")]
912            Abs => map!(abs::abs),
913            Negate => map!(dispatch::negate),
914            NullCount => {
915                let f = |s: &mut [Column]| {
916                    let s = &s[0];
917                    Ok(Some(Column::new(
918                        s.name().clone(),
919                        [s.null_count() as IdxSize],
920                    )))
921                };
922                wrap!(f)
923            },
924            Pow(func) => match func {
925                PowFunction::Generic => wrap!(pow::pow),
926                PowFunction::Sqrt => map!(pow::sqrt),
927                PowFunction::Cbrt => map!(pow::cbrt),
928            },
929            #[cfg(feature = "row_hash")]
930            Hash(k0, k1, k2, k3) => {
931                map!(row_hash::row_hash, k0, k1, k2, k3)
932            },
933            #[cfg(feature = "arg_where")]
934            ArgWhere => {
935                wrap!(arg_where::arg_where)
936            },
937            #[cfg(feature = "index_of")]
938            IndexOf => {
939                map_as_slice!(index_of::index_of)
940            },
941            #[cfg(feature = "search_sorted")]
942            SearchSorted(side) => {
943                map_as_slice!(search_sorted::search_sorted_impl, side)
944            },
945            #[cfg(feature = "range")]
946            Range(func) => func.into(),
947
948            #[cfg(feature = "trigonometry")]
949            Trigonometry(trig_function) => {
950                map!(trigonometry::apply_trigonometric_function, trig_function)
951            },
952            #[cfg(feature = "trigonometry")]
953            Atan2 => {
954                wrap!(trigonometry::apply_arctan2)
955            },
956
957            #[cfg(feature = "sign")]
958            Sign => {
959                map!(sign::sign)
960            },
961            FillNull => {
962                map_as_slice!(fill_null::fill_null)
963            },
964            #[cfg(feature = "rolling_window")]
965            RollingExpr(f) => {
966                use RollingFunction::*;
967                match f {
968                    Min(options) => map!(rolling::rolling_min, options.clone()),
969                    Max(options) => map!(rolling::rolling_max, options.clone()),
970                    Mean(options) => map!(rolling::rolling_mean, options.clone()),
971                    Sum(options) => map!(rolling::rolling_sum, options.clone()),
972                    Quantile(options) => map!(rolling::rolling_quantile, options.clone()),
973                    Var(options) => map!(rolling::rolling_var, options.clone()),
974                    Std(options) => map!(rolling::rolling_std, options.clone()),
975                    #[cfg(feature = "moment")]
976                    Skew(options) => map!(rolling::rolling_skew, options.clone()),
977                    #[cfg(feature = "moment")]
978                    Kurtosis(options) => map!(rolling::rolling_kurtosis, options.clone()),
979                    #[cfg(feature = "cov")]
980                    CorrCov {
981                        rolling_options,
982                        corr_cov_options,
983                        is_corr,
984                    } => {
985                        map_as_slice!(
986                            rolling::rolling_corr_cov,
987                            rolling_options.clone(),
988                            corr_cov_options,
989                            is_corr
990                        )
991                    },
992                }
993            },
994            #[cfg(feature = "rolling_window_by")]
995            RollingExprBy(f) => {
996                use RollingFunctionBy::*;
997                match f {
998                    MinBy(options) => map_as_slice!(rolling_by::rolling_min_by, options.clone()),
999                    MaxBy(options) => map_as_slice!(rolling_by::rolling_max_by, options.clone()),
1000                    MeanBy(options) => map_as_slice!(rolling_by::rolling_mean_by, options.clone()),
1001                    SumBy(options) => map_as_slice!(rolling_by::rolling_sum_by, options.clone()),
1002                    QuantileBy(options) => {
1003                        map_as_slice!(rolling_by::rolling_quantile_by, options.clone())
1004                    },
1005                    VarBy(options) => map_as_slice!(rolling_by::rolling_var_by, options.clone()),
1006                    StdBy(options) => map_as_slice!(rolling_by::rolling_std_by, options.clone()),
1007                }
1008            },
1009            #[cfg(feature = "hist")]
1010            Hist {
1011                bin_count,
1012                include_category,
1013                include_breakpoint,
1014            } => {
1015                map_as_slice!(
1016                    dispatch::hist,
1017                    bin_count,
1018                    include_category,
1019                    include_breakpoint
1020                )
1021            },
1022            ShiftAndFill => {
1023                map_as_slice!(shift_and_fill::shift_and_fill)
1024            },
1025            DropNans => map_owned!(nan::drop_nans),
1026            DropNulls => map!(dispatch::drop_nulls),
1027            #[cfg(feature = "round_series")]
1028            Clip { has_min, has_max } => {
1029                map_as_slice!(clip::clip, has_min, has_max)
1030            },
1031            #[cfg(feature = "mode")]
1032            Mode => map!(dispatch::mode),
1033            #[cfg(feature = "moment")]
1034            Skew(bias) => map!(dispatch::skew, bias),
1035            #[cfg(feature = "moment")]
1036            Kurtosis(fisher, bias) => map!(dispatch::kurtosis, fisher, bias),
1037            ArgUnique => map!(dispatch::arg_unique),
1038            Repeat => map_as_slice!(repeat::repeat),
1039            #[cfg(feature = "rank")]
1040            Rank { options, seed } => map!(dispatch::rank, options, seed),
1041            #[cfg(feature = "dtype-struct")]
1042            AsStruct => {
1043                map_as_slice!(coerce::as_struct)
1044            },
1045            #[cfg(feature = "top_k")]
1046            TopK { descending } => {
1047                map_as_slice!(top_k, descending)
1048            },
1049            #[cfg(feature = "top_k")]
1050            TopKBy { descending } => map_as_slice!(top_k_by, descending.clone()),
1051            Shift => map_as_slice!(shift_and_fill::shift),
1052            #[cfg(feature = "cum_agg")]
1053            CumCount { reverse } => map!(cum::cum_count, reverse),
1054            #[cfg(feature = "cum_agg")]
1055            CumSum { reverse } => map!(cum::cum_sum, reverse),
1056            #[cfg(feature = "cum_agg")]
1057            CumProd { reverse } => map!(cum::cum_prod, reverse),
1058            #[cfg(feature = "cum_agg")]
1059            CumMin { reverse } => map!(cum::cum_min, reverse),
1060            #[cfg(feature = "cum_agg")]
1061            CumMax { reverse } => map!(cum::cum_max, reverse),
1062            #[cfg(feature = "dtype-struct")]
1063            ValueCounts {
1064                sort,
1065                parallel,
1066                name,
1067                normalize,
1068            } => map!(
1069                dispatch::value_counts,
1070                sort,
1071                parallel,
1072                name.clone(),
1073                normalize
1074            ),
1075            #[cfg(feature = "unique_counts")]
1076            UniqueCounts => map!(dispatch::unique_counts),
1077            Reverse => map!(dispatch::reverse),
1078            #[cfg(feature = "approx_unique")]
1079            ApproxNUnique => map!(dispatch::approx_n_unique),
1080            Coalesce => map_as_slice!(fill_null::coalesce),
1081            ShrinkType => map_owned!(shrink_type::shrink),
1082            #[cfg(feature = "diff")]
1083            Diff(null_behavior) => map_as_slice!(dispatch::diff, null_behavior),
1084            #[cfg(feature = "pct_change")]
1085            PctChange => map_as_slice!(dispatch::pct_change),
1086            #[cfg(feature = "interpolate")]
1087            Interpolate(method) => {
1088                map!(dispatch::interpolate, method)
1089            },
1090            #[cfg(feature = "interpolate_by")]
1091            InterpolateBy => {
1092                map_as_slice!(dispatch::interpolate_by)
1093            },
1094            #[cfg(feature = "log")]
1095            Entropy { base, normalize } => map!(log::entropy, base, normalize),
1096            #[cfg(feature = "log")]
1097            Log { base } => map!(log::log, base),
1098            #[cfg(feature = "log")]
1099            Log1p => map!(log::log1p),
1100            #[cfg(feature = "log")]
1101            Exp => map!(log::exp),
1102            Unique(stable) => map!(unique::unique, stable),
1103            #[cfg(feature = "round_series")]
1104            Round { decimals, mode } => map!(round::round, decimals, mode),
1105            #[cfg(feature = "round_series")]
1106            RoundSF { digits } => map!(round::round_sig_figs, digits),
1107            #[cfg(feature = "round_series")]
1108            Floor => map!(round::floor),
1109            #[cfg(feature = "round_series")]
1110            Ceil => map!(round::ceil),
1111            UpperBound => map!(bounds::upper_bound),
1112            LowerBound => map!(bounds::lower_bound),
1113            #[cfg(feature = "fused")]
1114            Fused(op) => map_as_slice!(fused::fused, op),
1115            ConcatExpr(rechunk) => map_as_slice!(concat::concat_expr, rechunk),
1116            #[cfg(feature = "cov")]
1117            Correlation { method } => map_as_slice!(correlation::corr, method),
1118            #[cfg(feature = "peaks")]
1119            PeakMin => map!(peaks::peak_min),
1120            #[cfg(feature = "peaks")]
1121            PeakMax => map!(peaks::peak_max),
1122            #[cfg(feature = "repeat_by")]
1123            RepeatBy => map_as_slice!(dispatch::repeat_by),
1124            #[cfg(feature = "dtype-array")]
1125            Reshape(dims) => map!(dispatch::reshape, &dims),
1126            #[cfg(feature = "cutqcut")]
1127            Cut {
1128                breaks,
1129                labels,
1130                left_closed,
1131                include_breaks,
1132            } => map!(
1133                cut::cut,
1134                breaks.clone(),
1135                labels.clone(),
1136                left_closed,
1137                include_breaks
1138            ),
1139            #[cfg(feature = "cutqcut")]
1140            QCut {
1141                probs,
1142                labels,
1143                left_closed,
1144                allow_duplicates,
1145                include_breaks,
1146            } => map!(
1147                cut::qcut,
1148                probs.clone(),
1149                labels.clone(),
1150                left_closed,
1151                allow_duplicates,
1152                include_breaks
1153            ),
1154            #[cfg(feature = "rle")]
1155            RLE => map!(rle),
1156            #[cfg(feature = "rle")]
1157            RLEID => map!(rle_id),
1158            ToPhysical => map!(dispatch::to_physical),
1159            #[cfg(feature = "random")]
1160            Random { method, seed } => {
1161                use RandomMethod::*;
1162                match method {
1163                    Shuffle => map!(random::shuffle, seed),
1164                    Sample {
1165                        is_fraction,
1166                        with_replacement,
1167                        shuffle,
1168                    } => {
1169                        if is_fraction {
1170                            map_as_slice!(random::sample_frac, with_replacement, shuffle, seed)
1171                        } else {
1172                            map_as_slice!(random::sample_n, with_replacement, shuffle, seed)
1173                        }
1174                    },
1175                }
1176            },
1177            SetSortedFlag(sorted) => map!(dispatch::set_sorted_flag, sorted),
1178            #[cfg(feature = "ffi_plugin")]
1179            FfiPlugin {
1180                flags: _,
1181                lib,
1182                symbol,
1183                kwargs,
1184            } => unsafe {
1185                map_as_slice!(
1186                    plugin::call_plugin,
1187                    lib.as_ref(),
1188                    symbol.as_ref(),
1189                    kwargs.as_ref()
1190                )
1191            },
1192            MaxHorizontal => wrap!(dispatch::max_horizontal),
1193            MinHorizontal => wrap!(dispatch::min_horizontal),
1194            SumHorizontal { ignore_nulls } => wrap!(dispatch::sum_horizontal, ignore_nulls),
1195            MeanHorizontal { ignore_nulls } => wrap!(dispatch::mean_horizontal, ignore_nulls),
1196            #[cfg(feature = "ewma")]
1197            EwmMean { options } => map!(ewm::ewm_mean, options),
1198            #[cfg(feature = "ewma_by")]
1199            EwmMeanBy { half_life } => map_as_slice!(ewm_by::ewm_mean_by, half_life),
1200            #[cfg(feature = "ewma")]
1201            EwmStd { options } => map!(ewm::ewm_std, options),
1202            #[cfg(feature = "ewma")]
1203            EwmVar { options } => map!(ewm::ewm_var, options),
1204            #[cfg(feature = "replace")]
1205            Replace => {
1206                map_as_slice!(dispatch::replace)
1207            },
1208            #[cfg(feature = "replace")]
1209            ReplaceStrict { return_dtype } => {
1210                map_as_slice!(dispatch::replace_strict, return_dtype.clone())
1211            },
1212
1213            FillNullWithStrategy(strategy) => map!(dispatch::fill_null_with_strategy, strategy),
1214            GatherEvery { n, offset } => map!(dispatch::gather_every, n, offset),
1215            #[cfg(feature = "reinterpret")]
1216            Reinterpret(signed) => map!(dispatch::reinterpret, signed),
1217            ExtendConstant => map_as_slice!(dispatch::extend_constant),
1218        }
1219    }
1220}
1221
1222impl FunctionExpr {
1223    pub fn function_options(&self) -> FunctionOptions {
1224        use FunctionExpr as F;
1225        match self {
1226            #[cfg(feature = "dtype-array")]
1227            F::ArrayExpr(e) => e.function_options(),
1228            F::BinaryExpr(e) => e.function_options(),
1229            #[cfg(feature = "dtype-categorical")]
1230            F::Categorical(e) => e.function_options(),
1231            F::ListExpr(e) => e.function_options(),
1232            #[cfg(feature = "strings")]
1233            F::StringExpr(e) => e.function_options(),
1234            #[cfg(feature = "dtype-struct")]
1235            F::StructExpr(e) => e.function_options(),
1236            #[cfg(feature = "temporal")]
1237            F::TemporalExpr(e) => e.function_options(),
1238            #[cfg(feature = "bitwise")]
1239            F::Bitwise(e) => e.function_options(),
1240            F::Boolean(e) => e.function_options(),
1241            #[cfg(feature = "business")]
1242            F::Business(e) => e.function_options(),
1243            F::Pow(e) => e.function_options(),
1244            #[cfg(feature = "range")]
1245            F::Range(e) => e.function_options(),
1246            #[cfg(feature = "abs")]
1247            F::Abs => FunctionOptions::elementwise(),
1248            F::Negate => FunctionOptions::elementwise(),
1249            #[cfg(feature = "hist")]
1250            F::Hist { .. } => FunctionOptions::groupwise(),
1251            F::NullCount => FunctionOptions::aggregation(),
1252            #[cfg(feature = "row_hash")]
1253            F::Hash(_, _, _, _) => FunctionOptions::elementwise(),
1254            #[cfg(feature = "arg_where")]
1255            F::ArgWhere => FunctionOptions::groupwise(),
1256            #[cfg(feature = "index_of")]
1257            F::IndexOf => {
1258                FunctionOptions::aggregation().with_casting_rules(CastingRules::FirstArgLossless)
1259            },
1260            #[cfg(feature = "search_sorted")]
1261            F::SearchSorted(_) => FunctionOptions::groupwise().with_supertyping(
1262                (SuperTypeFlags::default() & !SuperTypeFlags::ALLOW_PRIMITIVE_TO_STRING).into(),
1263            ),
1264            #[cfg(feature = "trigonometry")]
1265            F::Trigonometry(_) => FunctionOptions::elementwise(),
1266            #[cfg(feature = "trigonometry")]
1267            F::Atan2 => FunctionOptions::elementwise(),
1268            #[cfg(feature = "sign")]
1269            F::Sign => FunctionOptions::elementwise(),
1270            F::FillNull => FunctionOptions::elementwise().with_supertyping(Default::default()),
1271            F::FillNullWithStrategy(strategy) if strategy.is_elementwise() => {
1272                FunctionOptions::elementwise()
1273            },
1274            F::FillNullWithStrategy(_) => FunctionOptions::groupwise(),
1275            #[cfg(feature = "rolling_window")]
1276            F::RollingExpr(_) => FunctionOptions::length_preserving(),
1277            #[cfg(feature = "rolling_window_by")]
1278            F::RollingExprBy(_) => FunctionOptions::length_preserving(),
1279            F::ShiftAndFill => FunctionOptions::length_preserving(),
1280            F::Shift => FunctionOptions::length_preserving(),
1281            F::DropNans => FunctionOptions::row_separable(),
1282            F::DropNulls => FunctionOptions::row_separable()
1283                .with_flags(|f| f | FunctionFlags::ALLOW_EMPTY_INPUTS),
1284            #[cfg(feature = "mode")]
1285            F::Mode => FunctionOptions::groupwise(),
1286            #[cfg(feature = "moment")]
1287            F::Skew(_) => FunctionOptions::aggregation(),
1288            #[cfg(feature = "moment")]
1289            F::Kurtosis(_, _) => FunctionOptions::aggregation(),
1290            #[cfg(feature = "dtype-array")]
1291            F::Reshape(_) => FunctionOptions::groupwise(),
1292            #[cfg(feature = "repeat_by")]
1293            F::RepeatBy => FunctionOptions::elementwise(),
1294            F::ArgUnique => FunctionOptions::groupwise(),
1295            #[cfg(feature = "rank")]
1296            F::Rank { .. } => FunctionOptions::groupwise(),
1297            F::Repeat => {
1298                FunctionOptions::groupwise().with_flags(|f| f | FunctionFlags::ALLOW_RENAME)
1299            },
1300            #[cfg(feature = "round_series")]
1301            F::Clip { .. } => FunctionOptions::elementwise(),
1302            #[cfg(feature = "dtype-struct")]
1303            F::AsStruct => FunctionOptions::elementwise().with_flags(|f| {
1304                f | FunctionFlags::PASS_NAME_TO_APPLY | FunctionFlags::INPUT_WILDCARD_EXPANSION
1305            }),
1306            #[cfg(feature = "top_k")]
1307            F::TopK { .. } => FunctionOptions::groupwise(),
1308            #[cfg(feature = "top_k")]
1309            F::TopKBy { .. } => FunctionOptions::groupwise(),
1310            #[cfg(feature = "cum_agg")]
1311            F::CumCount { .. }
1312            | F::CumSum { .. }
1313            | F::CumProd { .. }
1314            | F::CumMin { .. }
1315            | F::CumMax { .. } => FunctionOptions::length_preserving(),
1316            F::Reverse => FunctionOptions::length_preserving(),
1317            #[cfg(feature = "dtype-struct")]
1318            F::ValueCounts { .. } => {
1319                FunctionOptions::groupwise().with_flags(|f| f | FunctionFlags::PASS_NAME_TO_APPLY)
1320            },
1321            #[cfg(feature = "unique_counts")]
1322            F::UniqueCounts => FunctionOptions::groupwise(),
1323            #[cfg(feature = "approx_unique")]
1324            F::ApproxNUnique => FunctionOptions::aggregation(),
1325            F::Coalesce => FunctionOptions::elementwise()
1326                .with_flags(|f| f | FunctionFlags::INPUT_WILDCARD_EXPANSION)
1327                .with_supertyping(Default::default()),
1328            F::ShrinkType => FunctionOptions::length_preserving(),
1329            #[cfg(feature = "diff")]
1330            F::Diff(NullBehavior::Drop) => FunctionOptions::groupwise(),
1331            #[cfg(feature = "diff")]
1332            F::Diff(NullBehavior::Ignore) => FunctionOptions::length_preserving(),
1333            #[cfg(feature = "pct_change")]
1334            F::PctChange => FunctionOptions::length_preserving(),
1335            #[cfg(feature = "interpolate")]
1336            F::Interpolate(_) => FunctionOptions::length_preserving(),
1337            #[cfg(feature = "interpolate_by")]
1338            F::InterpolateBy => FunctionOptions::length_preserving(),
1339            #[cfg(feature = "log")]
1340            F::Log { .. } | F::Log1p | F::Exp => FunctionOptions::elementwise(),
1341            #[cfg(feature = "log")]
1342            F::Entropy { .. } => FunctionOptions::aggregation(),
1343            F::Unique(_) => FunctionOptions::groupwise(),
1344            #[cfg(feature = "round_series")]
1345            F::Round { .. } | F::RoundSF { .. } | F::Floor | F::Ceil => {
1346                FunctionOptions::elementwise()
1347            },
1348            F::UpperBound | F::LowerBound => FunctionOptions::aggregation(),
1349            #[cfg(feature = "fused")]
1350            F::Fused(_) => FunctionOptions::elementwise(),
1351            F::ConcatExpr(_) => FunctionOptions::groupwise()
1352                .with_flags(|f| f | FunctionFlags::INPUT_WILDCARD_EXPANSION)
1353                .with_supertyping(Default::default()),
1354            #[cfg(feature = "cov")]
1355            F::Correlation { .. } => {
1356                FunctionOptions::aggregation().with_supertyping(Default::default())
1357            },
1358            #[cfg(feature = "peaks")]
1359            F::PeakMin | F::PeakMax => FunctionOptions::length_preserving(),
1360            #[cfg(feature = "cutqcut")]
1361            F::Cut { .. } | F::QCut { .. } => FunctionOptions::length_preserving()
1362                .with_flags(|f| f | FunctionFlags::PASS_NAME_TO_APPLY),
1363            #[cfg(feature = "rle")]
1364            F::RLE => FunctionOptions::groupwise(),
1365            #[cfg(feature = "rle")]
1366            F::RLEID => FunctionOptions::length_preserving(),
1367            F::ToPhysical => FunctionOptions::elementwise(),
1368            #[cfg(feature = "random")]
1369            F::Random {
1370                method: RandomMethod::Sample { .. },
1371                ..
1372            } => FunctionOptions::groupwise(),
1373            #[cfg(feature = "random")]
1374            F::Random {
1375                method: RandomMethod::Shuffle,
1376                ..
1377            } => FunctionOptions::length_preserving(),
1378            F::SetSortedFlag(_) => FunctionOptions::elementwise(),
1379            #[cfg(feature = "ffi_plugin")]
1380            F::FfiPlugin { flags, .. } => *flags,
1381            F::MaxHorizontal | F::MinHorizontal => FunctionOptions::elementwise().with_flags(|f| {
1382                f | FunctionFlags::INPUT_WILDCARD_EXPANSION | FunctionFlags::ALLOW_RENAME
1383            }),
1384            F::MeanHorizontal { .. } | F::SumHorizontal { .. } => FunctionOptions::elementwise()
1385                .with_flags(|f| f | FunctionFlags::INPUT_WILDCARD_EXPANSION),
1386            #[cfg(feature = "ewma")]
1387            F::EwmMean { .. } | F::EwmStd { .. } | F::EwmVar { .. } => {
1388                FunctionOptions::length_preserving()
1389            },
1390            #[cfg(feature = "ewma_by")]
1391            F::EwmMeanBy { .. } => FunctionOptions::length_preserving(),
1392            #[cfg(feature = "replace")]
1393            F::Replace => FunctionOptions::elementwise(),
1394            #[cfg(feature = "replace")]
1395            F::ReplaceStrict { .. } => FunctionOptions::elementwise(),
1396            F::GatherEvery { .. } => FunctionOptions::groupwise(),
1397            #[cfg(feature = "reinterpret")]
1398            F::Reinterpret(_) => FunctionOptions::elementwise(),
1399            F::ExtendConstant => FunctionOptions::groupwise(),
1400        }
1401    }
1402}