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(crate) use array::ArrayFunction;
83#[cfg(feature = "cov")]
84pub(crate) use correlation::CorrelationMethod;
85#[cfg(feature = "fused")]
86pub(crate) use fused::FusedOperator;
87pub(crate) use list::ListFunction;
88use polars_core::datatypes::ReshapeDimension;
89use polars_core::prelude::*;
90#[cfg(feature = "random")]
91pub(crate) use random::RandomMethod;
92use schema::FieldsMapper;
93#[cfg(feature = "serde")]
94use serde::{Deserialize, Serialize};
95
96pub(crate) use self::binary::BinaryFunction;
97#[cfg(feature = "bitwise")]
98pub use self::bitwise::BitwiseFunction;
99pub use self::boolean::BooleanFunction;
100#[cfg(feature = "business")]
101pub(super) 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(super) 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(i64, 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    },
273    #[cfg(feature = "round_series")]
274    RoundSF {
275        digits: i32,
276    },
277    #[cfg(feature = "round_series")]
278    Floor,
279    #[cfg(feature = "round_series")]
280    Ceil,
281    UpperBound,
282    LowerBound,
283    #[cfg(feature = "fused")]
284    Fused(fused::FusedOperator),
285    ConcatExpr(bool),
286    #[cfg(feature = "cov")]
287    Correlation {
288        method: correlation::CorrelationMethod,
289    },
290    #[cfg(feature = "peaks")]
291    PeakMin,
292    #[cfg(feature = "peaks")]
293    PeakMax,
294    #[cfg(feature = "cutqcut")]
295    Cut {
296        breaks: Vec<f64>,
297        labels: Option<Vec<PlSmallStr>>,
298        left_closed: bool,
299        include_breaks: bool,
300    },
301    #[cfg(feature = "cutqcut")]
302    QCut {
303        probs: Vec<f64>,
304        labels: Option<Vec<PlSmallStr>>,
305        left_closed: bool,
306        allow_duplicates: bool,
307        include_breaks: bool,
308    },
309    #[cfg(feature = "rle")]
310    RLE,
311    #[cfg(feature = "rle")]
312    RLEID,
313    ToPhysical,
314    #[cfg(feature = "random")]
315    Random {
316        method: random::RandomMethod,
317        seed: Option<u64>,
318    },
319    SetSortedFlag(IsSorted),
320    #[cfg(feature = "ffi_plugin")]
321    /// Creating this node is unsafe
322    /// This will lead to calls over FFI.
323    FfiPlugin {
324        /// Shared library.
325        lib: PlSmallStr,
326        /// Identifier in the shared lib.
327        symbol: PlSmallStr,
328        /// Pickle serialized keyword arguments.
329        kwargs: Arc<[u8]>,
330    },
331    BackwardFill {
332        limit: FillNullLimit,
333    },
334    ForwardFill {
335        limit: FillNullLimit,
336    },
337    MaxHorizontal,
338    MinHorizontal,
339    SumHorizontal {
340        ignore_nulls: bool,
341    },
342    MeanHorizontal {
343        ignore_nulls: bool,
344    },
345    #[cfg(feature = "ewma")]
346    EwmMean {
347        options: EWMOptions,
348    },
349    #[cfg(feature = "ewma_by")]
350    EwmMeanBy {
351        half_life: Duration,
352    },
353    #[cfg(feature = "ewma")]
354    EwmStd {
355        options: EWMOptions,
356    },
357    #[cfg(feature = "ewma")]
358    EwmVar {
359        options: EWMOptions,
360    },
361    #[cfg(feature = "replace")]
362    Replace,
363    #[cfg(feature = "replace")]
364    ReplaceStrict {
365        return_dtype: Option<DataType>,
366    },
367    GatherEvery {
368        n: usize,
369        offset: usize,
370    },
371    #[cfg(feature = "reinterpret")]
372    Reinterpret(bool),
373    ExtendConstant,
374}
375
376impl Hash for FunctionExpr {
377    fn hash<H: Hasher>(&self, state: &mut H) {
378        std::mem::discriminant(self).hash(state);
379        use FunctionExpr::*;
380        match self {
381            // Namespaces
382            #[cfg(feature = "dtype-array")]
383            ArrayExpr(f) => f.hash(state),
384            BinaryExpr(f) => f.hash(state),
385            #[cfg(feature = "dtype-categorical")]
386            Categorical(f) => f.hash(state),
387            ListExpr(f) => f.hash(state),
388            #[cfg(feature = "strings")]
389            StringExpr(f) => f.hash(state),
390            #[cfg(feature = "dtype-struct")]
391            StructExpr(f) => f.hash(state),
392            #[cfg(feature = "temporal")]
393            TemporalExpr(f) => f.hash(state),
394            #[cfg(feature = "bitwise")]
395            Bitwise(f) => f.hash(state),
396
397            // Other expressions
398            Boolean(f) => f.hash(state),
399            #[cfg(feature = "business")]
400            Business(f) => f.hash(state),
401            Pow(f) => f.hash(state),
402            #[cfg(feature = "index_of")]
403            IndexOf => {},
404            #[cfg(feature = "search_sorted")]
405            SearchSorted(f) => f.hash(state),
406            #[cfg(feature = "random")]
407            Random { method, .. } => method.hash(state),
408            #[cfg(feature = "cov")]
409            Correlation { method, .. } => method.hash(state),
410            #[cfg(feature = "range")]
411            Range(f) => f.hash(state),
412            #[cfg(feature = "trigonometry")]
413            Trigonometry(f) => f.hash(state),
414            #[cfg(feature = "fused")]
415            Fused(f) => f.hash(state),
416            #[cfg(feature = "diff")]
417            Diff(_, null_behavior) => null_behavior.hash(state),
418            #[cfg(feature = "interpolate")]
419            Interpolate(f) => f.hash(state),
420            #[cfg(feature = "interpolate_by")]
421            InterpolateBy => {},
422            #[cfg(feature = "ffi_plugin")]
423            FfiPlugin {
424                lib,
425                symbol,
426                kwargs,
427            } => {
428                kwargs.hash(state);
429                lib.hash(state);
430                symbol.hash(state);
431            },
432            MaxHorizontal
433            | MinHorizontal
434            | SumHorizontal { .. }
435            | MeanHorizontal { .. }
436            | DropNans
437            | DropNulls
438            | Reverse
439            | ArgUnique
440            | Shift
441            | ShiftAndFill => {},
442            #[cfg(feature = "mode")]
443            Mode => {},
444            #[cfg(feature = "abs")]
445            Abs => {},
446            Negate => {},
447            NullCount => {},
448            #[cfg(feature = "arg_where")]
449            ArgWhere => {},
450            #[cfg(feature = "trigonometry")]
451            Atan2 => {},
452            #[cfg(feature = "dtype-struct")]
453            AsStruct => {},
454            #[cfg(feature = "sign")]
455            Sign => {},
456            #[cfg(feature = "row_hash")]
457            Hash(a, b, c, d) => (a, b, c, d).hash(state),
458            FillNull => {},
459            #[cfg(feature = "rolling_window")]
460            RollingExpr(f) => {
461                f.hash(state);
462            },
463            #[cfg(feature = "rolling_window_by")]
464            RollingExprBy(f) => {
465                f.hash(state);
466            },
467            #[cfg(feature = "moment")]
468            Skew(a) => a.hash(state),
469            #[cfg(feature = "moment")]
470            Kurtosis(a, b) => {
471                a.hash(state);
472                b.hash(state);
473            },
474            Repeat => {},
475            #[cfg(feature = "rank")]
476            Rank { options, seed } => {
477                options.hash(state);
478                seed.hash(state);
479            },
480            #[cfg(feature = "round_series")]
481            Clip { has_min, has_max } => {
482                has_min.hash(state);
483                has_max.hash(state);
484            },
485            #[cfg(feature = "top_k")]
486            TopK { descending } => descending.hash(state),
487            #[cfg(feature = "cum_agg")]
488            CumCount { reverse } => reverse.hash(state),
489            #[cfg(feature = "cum_agg")]
490            CumSum { reverse } => reverse.hash(state),
491            #[cfg(feature = "cum_agg")]
492            CumProd { reverse } => reverse.hash(state),
493            #[cfg(feature = "cum_agg")]
494            CumMin { reverse } => reverse.hash(state),
495            #[cfg(feature = "cum_agg")]
496            CumMax { reverse } => reverse.hash(state),
497            #[cfg(feature = "dtype-struct")]
498            ValueCounts {
499                sort,
500                parallel,
501                name,
502                normalize,
503            } => {
504                sort.hash(state);
505                parallel.hash(state);
506                name.hash(state);
507                normalize.hash(state);
508            },
509            #[cfg(feature = "unique_counts")]
510            UniqueCounts => {},
511            #[cfg(feature = "approx_unique")]
512            ApproxNUnique => {},
513            Coalesce => {},
514            ShrinkType => {},
515            #[cfg(feature = "pct_change")]
516            PctChange => {},
517            #[cfg(feature = "log")]
518            Entropy { base, normalize } => {
519                base.to_bits().hash(state);
520                normalize.hash(state);
521            },
522            #[cfg(feature = "log")]
523            Log { base } => base.to_bits().hash(state),
524            #[cfg(feature = "log")]
525            Log1p => {},
526            #[cfg(feature = "log")]
527            Exp => {},
528            Unique(a) => a.hash(state),
529            #[cfg(feature = "round_series")]
530            Round { decimals } => decimals.hash(state),
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            BackwardFill { limit } | ForwardFill { limit } => limit.hash(state),
583            #[cfg(feature = "ewma")]
584            EwmMean { options } => options.hash(state),
585            #[cfg(feature = "ewma_by")]
586            EwmMeanBy { half_life } => (half_life).hash(state),
587            #[cfg(feature = "ewma")]
588            EwmStd { options } => options.hash(state),
589            #[cfg(feature = "ewma")]
590            EwmVar { options } => options.hash(state),
591            #[cfg(feature = "hist")]
592            Hist {
593                bin_count,
594                include_category,
595                include_breakpoint,
596            } => {
597                bin_count.hash(state);
598                include_category.hash(state);
599                include_breakpoint.hash(state);
600            },
601            #[cfg(feature = "replace")]
602            Replace => {},
603            #[cfg(feature = "replace")]
604            ReplaceStrict { return_dtype } => return_dtype.hash(state),
605            FillNullWithStrategy(strategy) => strategy.hash(state),
606            GatherEvery { n, offset } => (n, offset).hash(state),
607            #[cfg(feature = "reinterpret")]
608            Reinterpret(signed) => signed.hash(state),
609            ExtendConstant => {},
610            #[cfg(feature = "top_k")]
611            TopKBy { descending } => descending.hash(state),
612        }
613    }
614}
615
616impl Display for FunctionExpr {
617    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
618        use FunctionExpr::*;
619        let s = match self {
620            // Namespaces
621            #[cfg(feature = "dtype-array")]
622            ArrayExpr(func) => return write!(f, "{func}"),
623            BinaryExpr(func) => return write!(f, "{func}"),
624            #[cfg(feature = "dtype-categorical")]
625            Categorical(func) => return write!(f, "{func}"),
626            ListExpr(func) => return write!(f, "{func}"),
627            #[cfg(feature = "strings")]
628            StringExpr(func) => return write!(f, "{func}"),
629            #[cfg(feature = "dtype-struct")]
630            StructExpr(func) => return write!(f, "{func}"),
631            #[cfg(feature = "temporal")]
632            TemporalExpr(func) => return write!(f, "{func}"),
633            #[cfg(feature = "bitwise")]
634            Bitwise(func) => return write!(f, "bitwise_{func}"),
635
636            // Other expressions
637            Boolean(func) => return write!(f, "{func}"),
638            #[cfg(feature = "business")]
639            Business(func) => return write!(f, "{func}"),
640            #[cfg(feature = "abs")]
641            Abs => "abs",
642            Negate => "negate",
643            NullCount => "null_count",
644            Pow(func) => return write!(f, "{func}"),
645            #[cfg(feature = "row_hash")]
646            Hash(_, _, _, _) => "hash",
647            #[cfg(feature = "arg_where")]
648            ArgWhere => "arg_where",
649            #[cfg(feature = "index_of")]
650            IndexOf => "index_of",
651            #[cfg(feature = "search_sorted")]
652            SearchSorted(_) => "search_sorted",
653            #[cfg(feature = "range")]
654            Range(func) => return write!(f, "{func}"),
655            #[cfg(feature = "trigonometry")]
656            Trigonometry(func) => return write!(f, "{func}"),
657            #[cfg(feature = "trigonometry")]
658            Atan2 => return write!(f, "arctan2"),
659            #[cfg(feature = "sign")]
660            Sign => "sign",
661            FillNull { .. } => "fill_null",
662            #[cfg(feature = "rolling_window")]
663            RollingExpr(func, ..) => return write!(f, "{func}"),
664            #[cfg(feature = "rolling_window_by")]
665            RollingExprBy(func, ..) => return write!(f, "{func}"),
666            ShiftAndFill => "shift_and_fill",
667            DropNans => "drop_nans",
668            DropNulls => "drop_nulls",
669            #[cfg(feature = "mode")]
670            Mode => "mode",
671            #[cfg(feature = "moment")]
672            Skew(_) => "skew",
673            #[cfg(feature = "moment")]
674            Kurtosis(..) => "kurtosis",
675            ArgUnique => "arg_unique",
676            Repeat => "repeat",
677            #[cfg(feature = "rank")]
678            Rank { .. } => "rank",
679            #[cfg(feature = "round_series")]
680            Clip { has_min, has_max } => match (has_min, has_max) {
681                (true, true) => "clip",
682                (false, true) => "clip_max",
683                (true, false) => "clip_min",
684                _ => unreachable!(),
685            },
686            #[cfg(feature = "dtype-struct")]
687            AsStruct => "as_struct",
688            #[cfg(feature = "top_k")]
689            TopK { descending } => {
690                if *descending {
691                    "bottom_k"
692                } else {
693                    "top_k"
694                }
695            },
696            #[cfg(feature = "top_k")]
697            TopKBy { .. } => "top_k_by",
698            Shift => "shift",
699            #[cfg(feature = "cum_agg")]
700            CumCount { .. } => "cum_count",
701            #[cfg(feature = "cum_agg")]
702            CumSum { .. } => "cum_sum",
703            #[cfg(feature = "cum_agg")]
704            CumProd { .. } => "cum_prod",
705            #[cfg(feature = "cum_agg")]
706            CumMin { .. } => "cum_min",
707            #[cfg(feature = "cum_agg")]
708            CumMax { .. } => "cum_max",
709            #[cfg(feature = "dtype-struct")]
710            ValueCounts { .. } => "value_counts",
711            #[cfg(feature = "unique_counts")]
712            UniqueCounts => "unique_counts",
713            Reverse => "reverse",
714            #[cfg(feature = "approx_unique")]
715            ApproxNUnique => "approx_n_unique",
716            Coalesce => "coalesce",
717            ShrinkType => "shrink_dtype",
718            #[cfg(feature = "diff")]
719            Diff(_, _) => "diff",
720            #[cfg(feature = "pct_change")]
721            PctChange => "pct_change",
722            #[cfg(feature = "interpolate")]
723            Interpolate(_) => "interpolate",
724            #[cfg(feature = "interpolate_by")]
725            InterpolateBy => "interpolate_by",
726            #[cfg(feature = "log")]
727            Entropy { .. } => "entropy",
728            #[cfg(feature = "log")]
729            Log { .. } => "log",
730            #[cfg(feature = "log")]
731            Log1p => "log1p",
732            #[cfg(feature = "log")]
733            Exp => "exp",
734            Unique(stable) => {
735                if *stable {
736                    "unique_stable"
737                } else {
738                    "unique"
739                }
740            },
741            #[cfg(feature = "round_series")]
742            Round { .. } => "round",
743            #[cfg(feature = "round_series")]
744            RoundSF { .. } => "round_sig_figs",
745            #[cfg(feature = "round_series")]
746            Floor => "floor",
747            #[cfg(feature = "round_series")]
748            Ceil => "ceil",
749            UpperBound => "upper_bound",
750            LowerBound => "lower_bound",
751            #[cfg(feature = "fused")]
752            Fused(fused) => return Display::fmt(fused, f),
753            ConcatExpr(_) => "concat_expr",
754            #[cfg(feature = "cov")]
755            Correlation { method, .. } => return Display::fmt(method, f),
756            #[cfg(feature = "peaks")]
757            PeakMin => "peak_min",
758            #[cfg(feature = "peaks")]
759            PeakMax => "peak_max",
760            #[cfg(feature = "cutqcut")]
761            Cut { .. } => "cut",
762            #[cfg(feature = "cutqcut")]
763            QCut { .. } => "qcut",
764            #[cfg(feature = "dtype-array")]
765            Reshape(_) => "reshape",
766            #[cfg(feature = "repeat_by")]
767            RepeatBy => "repeat_by",
768            #[cfg(feature = "rle")]
769            RLE => "rle",
770            #[cfg(feature = "rle")]
771            RLEID => "rle_id",
772            ToPhysical => "to_physical",
773            #[cfg(feature = "random")]
774            Random { method, .. } => method.into(),
775            SetSortedFlag(_) => "set_sorted",
776            #[cfg(feature = "ffi_plugin")]
777            FfiPlugin { lib, symbol, .. } => return write!(f, "{lib}:{symbol}"),
778            BackwardFill { .. } => "backward_fill",
779            ForwardFill { .. } => "forward_fill",
780            MaxHorizontal => "max_horizontal",
781            MinHorizontal => "min_horizontal",
782            SumHorizontal { .. } => "sum_horizontal",
783            MeanHorizontal { .. } => "mean_horizontal",
784            #[cfg(feature = "ewma")]
785            EwmMean { .. } => "ewm_mean",
786            #[cfg(feature = "ewma_by")]
787            EwmMeanBy { .. } => "ewm_mean_by",
788            #[cfg(feature = "ewma")]
789            EwmStd { .. } => "ewm_std",
790            #[cfg(feature = "ewma")]
791            EwmVar { .. } => "ewm_var",
792            #[cfg(feature = "hist")]
793            Hist { .. } => "hist",
794            #[cfg(feature = "replace")]
795            Replace => "replace",
796            #[cfg(feature = "replace")]
797            ReplaceStrict { .. } => "replace_strict",
798            FillNullWithStrategy(_) => "fill_null_with_strategy",
799            GatherEvery { .. } => "gather_every",
800            #[cfg(feature = "reinterpret")]
801            Reinterpret(_) => "reinterpret",
802            ExtendConstant => "extend_constant",
803        };
804        write!(f, "{s}")
805    }
806}
807
808#[macro_export]
809macro_rules! wrap {
810    ($e:expr) => {
811        SpecialEq::new(Arc::new($e))
812    };
813
814    ($e:expr, $($args:expr),*) => {{
815        let f = move |s: &mut [Column]| {
816            $e(s, $($args),*)
817        };
818
819        SpecialEq::new(Arc::new(f))
820    }};
821}
822
823/// `Fn(&[Column], args)`
824/// * all expression arguments are in the slice.
825/// * the first element is the root expression.
826#[macro_export]
827macro_rules! map_as_slice {
828    ($func:path) => {{
829        let f = move |s: &mut [Column]| {
830            $func(s).map(Some)
831        };
832
833        SpecialEq::new(Arc::new(f))
834    }};
835
836    ($func:path, $($args:expr),*) => {{
837        let f = move |s: &mut [Column]| {
838            $func(s, $($args),*).map(Some)
839        };
840
841        SpecialEq::new(Arc::new(f))
842    }};
843}
844
845/// * `FnOnce(Series)`
846/// * `FnOnce(Series, args)`
847#[macro_export]
848macro_rules! map_owned {
849    ($func:path) => {{
850        let f = move |c: &mut [Column]| {
851            let c = std::mem::take(&mut c[0]);
852            $func(c).map(Some)
853        };
854
855        SpecialEq::new(Arc::new(f))
856    }};
857
858    ($func:path, $($args:expr),*) => {{
859        let f = move |c: &mut [Column]| {
860            let c = std::mem::take(&mut c[0]);
861            $func(c, $($args),*).map(Some)
862        };
863
864        SpecialEq::new(Arc::new(f))
865    }};
866}
867
868/// `Fn(&Series, args)`
869#[macro_export]
870macro_rules! map {
871    ($func:path) => {{
872        let f = move |c: &mut [Column]| {
873            let c = &c[0];
874            $func(c).map(Some)
875        };
876
877        SpecialEq::new(Arc::new(f))
878    }};
879
880    ($func:path, $($args:expr),*) => {{
881        let f = move |c: &mut [Column]| {
882            let c = &c[0];
883            $func(c, $($args),*).map(Some)
884        };
885
886        SpecialEq::new(Arc::new(f))
887    }};
888}
889
890impl From<FunctionExpr> for SpecialEq<Arc<dyn ColumnsUdf>> {
891    fn from(func: FunctionExpr) -> Self {
892        use FunctionExpr::*;
893        match func {
894            // Namespaces
895            #[cfg(feature = "dtype-array")]
896            ArrayExpr(func) => func.into(),
897            BinaryExpr(func) => func.into(),
898            #[cfg(feature = "dtype-categorical")]
899            Categorical(func) => func.into(),
900            ListExpr(func) => func.into(),
901            #[cfg(feature = "strings")]
902            StringExpr(func) => func.into(),
903            #[cfg(feature = "dtype-struct")]
904            StructExpr(func) => func.into(),
905            #[cfg(feature = "temporal")]
906            TemporalExpr(func) => func.into(),
907            #[cfg(feature = "bitwise")]
908            Bitwise(func) => func.into(),
909
910            // Other expressions
911            Boolean(func) => func.into(),
912            #[cfg(feature = "business")]
913            Business(func) => func.into(),
914            #[cfg(feature = "abs")]
915            Abs => map!(abs::abs),
916            Negate => map!(dispatch::negate),
917            NullCount => {
918                let f = |s: &mut [Column]| {
919                    let s = &s[0];
920                    Ok(Some(Column::new(
921                        s.name().clone(),
922                        [s.null_count() as IdxSize],
923                    )))
924                };
925                wrap!(f)
926            },
927            Pow(func) => match func {
928                PowFunction::Generic => wrap!(pow::pow),
929                PowFunction::Sqrt => map!(pow::sqrt),
930                PowFunction::Cbrt => map!(pow::cbrt),
931            },
932            #[cfg(feature = "row_hash")]
933            Hash(k0, k1, k2, k3) => {
934                map!(row_hash::row_hash, k0, k1, k2, k3)
935            },
936            #[cfg(feature = "arg_where")]
937            ArgWhere => {
938                wrap!(arg_where::arg_where)
939            },
940            #[cfg(feature = "index_of")]
941            IndexOf => {
942                map_as_slice!(index_of::index_of)
943            },
944            #[cfg(feature = "search_sorted")]
945            SearchSorted(side) => {
946                map_as_slice!(search_sorted::search_sorted_impl, side)
947            },
948            #[cfg(feature = "range")]
949            Range(func) => func.into(),
950
951            #[cfg(feature = "trigonometry")]
952            Trigonometry(trig_function) => {
953                map!(trigonometry::apply_trigonometric_function, trig_function)
954            },
955            #[cfg(feature = "trigonometry")]
956            Atan2 => {
957                wrap!(trigonometry::apply_arctan2)
958            },
959
960            #[cfg(feature = "sign")]
961            Sign => {
962                map!(sign::sign)
963            },
964            FillNull => {
965                map_as_slice!(fill_null::fill_null)
966            },
967            #[cfg(feature = "rolling_window")]
968            RollingExpr(f) => {
969                use RollingFunction::*;
970                match f {
971                    Min(options) => map!(rolling::rolling_min, options.clone()),
972                    Max(options) => map!(rolling::rolling_max, options.clone()),
973                    Mean(options) => map!(rolling::rolling_mean, options.clone()),
974                    Sum(options) => map!(rolling::rolling_sum, options.clone()),
975                    Quantile(options) => map!(rolling::rolling_quantile, options.clone()),
976                    Var(options) => map!(rolling::rolling_var, options.clone()),
977                    Std(options) => map!(rolling::rolling_std, options.clone()),
978                    #[cfg(feature = "moment")]
979                    Skew(window_size, bias) => map!(rolling::rolling_skew, window_size, bias),
980                    #[cfg(feature = "cov")]
981                    CorrCov {
982                        rolling_options,
983                        corr_cov_options,
984                        is_corr,
985                    } => {
986                        map_as_slice!(
987                            rolling::rolling_corr_cov,
988                            rolling_options.clone(),
989                            corr_cov_options,
990                            is_corr
991                        )
992                    },
993                }
994            },
995            #[cfg(feature = "rolling_window_by")]
996            RollingExprBy(f) => {
997                use RollingFunctionBy::*;
998                match f {
999                    MinBy(options) => map_as_slice!(rolling_by::rolling_min_by, options.clone()),
1000                    MaxBy(options) => map_as_slice!(rolling_by::rolling_max_by, options.clone()),
1001                    MeanBy(options) => map_as_slice!(rolling_by::rolling_mean_by, options.clone()),
1002                    SumBy(options) => map_as_slice!(rolling_by::rolling_sum_by, options.clone()),
1003                    QuantileBy(options) => {
1004                        map_as_slice!(rolling_by::rolling_quantile_by, options.clone())
1005                    },
1006                    VarBy(options) => map_as_slice!(rolling_by::rolling_var_by, options.clone()),
1007                    StdBy(options) => map_as_slice!(rolling_by::rolling_std_by, options.clone()),
1008                }
1009            },
1010            #[cfg(feature = "hist")]
1011            Hist {
1012                bin_count,
1013                include_category,
1014                include_breakpoint,
1015            } => {
1016                map_as_slice!(
1017                    dispatch::hist,
1018                    bin_count,
1019                    include_category,
1020                    include_breakpoint
1021                )
1022            },
1023            ShiftAndFill => {
1024                map_as_slice!(shift_and_fill::shift_and_fill)
1025            },
1026            DropNans => map_owned!(nan::drop_nans),
1027            DropNulls => map!(dispatch::drop_nulls),
1028            #[cfg(feature = "round_series")]
1029            Clip { has_min, has_max } => {
1030                map_as_slice!(clip::clip, has_min, has_max)
1031            },
1032            #[cfg(feature = "mode")]
1033            Mode => map!(dispatch::mode),
1034            #[cfg(feature = "moment")]
1035            Skew(bias) => map!(dispatch::skew, bias),
1036            #[cfg(feature = "moment")]
1037            Kurtosis(fisher, bias) => map!(dispatch::kurtosis, fisher, bias),
1038            ArgUnique => map!(dispatch::arg_unique),
1039            Repeat => map_as_slice!(repeat::repeat),
1040            #[cfg(feature = "rank")]
1041            Rank { options, seed } => map!(dispatch::rank, options, seed),
1042            #[cfg(feature = "dtype-struct")]
1043            AsStruct => {
1044                map_as_slice!(coerce::as_struct)
1045            },
1046            #[cfg(feature = "top_k")]
1047            TopK { descending } => {
1048                map_as_slice!(top_k, descending)
1049            },
1050            #[cfg(feature = "top_k")]
1051            TopKBy { descending } => map_as_slice!(top_k_by, descending.clone()),
1052            Shift => map_as_slice!(shift_and_fill::shift),
1053            #[cfg(feature = "cum_agg")]
1054            CumCount { reverse } => map!(cum::cum_count, reverse),
1055            #[cfg(feature = "cum_agg")]
1056            CumSum { reverse } => map!(cum::cum_sum, reverse),
1057            #[cfg(feature = "cum_agg")]
1058            CumProd { reverse } => map!(cum::cum_prod, reverse),
1059            #[cfg(feature = "cum_agg")]
1060            CumMin { reverse } => map!(cum::cum_min, reverse),
1061            #[cfg(feature = "cum_agg")]
1062            CumMax { reverse } => map!(cum::cum_max, reverse),
1063            #[cfg(feature = "dtype-struct")]
1064            ValueCounts {
1065                sort,
1066                parallel,
1067                name,
1068                normalize,
1069            } => map!(
1070                dispatch::value_counts,
1071                sort,
1072                parallel,
1073                name.clone(),
1074                normalize
1075            ),
1076            #[cfg(feature = "unique_counts")]
1077            UniqueCounts => map!(dispatch::unique_counts),
1078            Reverse => map!(dispatch::reverse),
1079            #[cfg(feature = "approx_unique")]
1080            ApproxNUnique => map!(dispatch::approx_n_unique),
1081            Coalesce => map_as_slice!(fill_null::coalesce),
1082            ShrinkType => map_owned!(shrink_type::shrink),
1083            #[cfg(feature = "diff")]
1084            Diff(n, null_behavior) => map!(dispatch::diff, n, null_behavior),
1085            #[cfg(feature = "pct_change")]
1086            PctChange => map_as_slice!(dispatch::pct_change),
1087            #[cfg(feature = "interpolate")]
1088            Interpolate(method) => {
1089                map!(dispatch::interpolate, method)
1090            },
1091            #[cfg(feature = "interpolate_by")]
1092            InterpolateBy => {
1093                map_as_slice!(dispatch::interpolate_by)
1094            },
1095            #[cfg(feature = "log")]
1096            Entropy { base, normalize } => map!(log::entropy, base, normalize),
1097            #[cfg(feature = "log")]
1098            Log { base } => map!(log::log, base),
1099            #[cfg(feature = "log")]
1100            Log1p => map!(log::log1p),
1101            #[cfg(feature = "log")]
1102            Exp => map!(log::exp),
1103            Unique(stable) => map!(unique::unique, stable),
1104            #[cfg(feature = "round_series")]
1105            Round { decimals } => map!(round::round, decimals),
1106            #[cfg(feature = "round_series")]
1107            RoundSF { digits } => map!(round::round_sig_figs, digits),
1108            #[cfg(feature = "round_series")]
1109            Floor => map!(round::floor),
1110            #[cfg(feature = "round_series")]
1111            Ceil => map!(round::ceil),
1112            UpperBound => map!(bounds::upper_bound),
1113            LowerBound => map!(bounds::lower_bound),
1114            #[cfg(feature = "fused")]
1115            Fused(op) => map_as_slice!(fused::fused, op),
1116            ConcatExpr(rechunk) => map_as_slice!(concat::concat_expr, rechunk),
1117            #[cfg(feature = "cov")]
1118            Correlation { method } => map_as_slice!(correlation::corr, method),
1119            #[cfg(feature = "peaks")]
1120            PeakMin => map!(peaks::peak_min),
1121            #[cfg(feature = "peaks")]
1122            PeakMax => map!(peaks::peak_max),
1123            #[cfg(feature = "repeat_by")]
1124            RepeatBy => map_as_slice!(dispatch::repeat_by),
1125            #[cfg(feature = "dtype-array")]
1126            Reshape(dims) => map!(dispatch::reshape, &dims),
1127            #[cfg(feature = "cutqcut")]
1128            Cut {
1129                breaks,
1130                labels,
1131                left_closed,
1132                include_breaks,
1133            } => map!(
1134                cut::cut,
1135                breaks.clone(),
1136                labels.clone(),
1137                left_closed,
1138                include_breaks
1139            ),
1140            #[cfg(feature = "cutqcut")]
1141            QCut {
1142                probs,
1143                labels,
1144                left_closed,
1145                allow_duplicates,
1146                include_breaks,
1147            } => map!(
1148                cut::qcut,
1149                probs.clone(),
1150                labels.clone(),
1151                left_closed,
1152                allow_duplicates,
1153                include_breaks
1154            ),
1155            #[cfg(feature = "rle")]
1156            RLE => map!(rle),
1157            #[cfg(feature = "rle")]
1158            RLEID => map!(rle_id),
1159            ToPhysical => map!(dispatch::to_physical),
1160            #[cfg(feature = "random")]
1161            Random { method, seed } => {
1162                use RandomMethod::*;
1163                match method {
1164                    Shuffle => map!(random::shuffle, seed),
1165                    Sample {
1166                        is_fraction,
1167                        with_replacement,
1168                        shuffle,
1169                    } => {
1170                        if is_fraction {
1171                            map_as_slice!(random::sample_frac, with_replacement, shuffle, seed)
1172                        } else {
1173                            map_as_slice!(random::sample_n, with_replacement, shuffle, seed)
1174                        }
1175                    },
1176                }
1177            },
1178            SetSortedFlag(sorted) => map!(dispatch::set_sorted_flag, sorted),
1179            #[cfg(feature = "ffi_plugin")]
1180            FfiPlugin {
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            BackwardFill { limit } => map!(dispatch::backward_fill, limit),
1193            ForwardFill { limit } => map!(dispatch::forward_fill, limit),
1194            MaxHorizontal => wrap!(dispatch::max_horizontal),
1195            MinHorizontal => wrap!(dispatch::min_horizontal),
1196            SumHorizontal { ignore_nulls } => wrap!(dispatch::sum_horizontal, ignore_nulls),
1197            MeanHorizontal { ignore_nulls } => wrap!(dispatch::mean_horizontal, ignore_nulls),
1198            #[cfg(feature = "ewma")]
1199            EwmMean { options } => map!(ewm::ewm_mean, options),
1200            #[cfg(feature = "ewma_by")]
1201            EwmMeanBy { half_life } => map_as_slice!(ewm_by::ewm_mean_by, half_life),
1202            #[cfg(feature = "ewma")]
1203            EwmStd { options } => map!(ewm::ewm_std, options),
1204            #[cfg(feature = "ewma")]
1205            EwmVar { options } => map!(ewm::ewm_var, options),
1206            #[cfg(feature = "replace")]
1207            Replace => {
1208                map_as_slice!(dispatch::replace)
1209            },
1210            #[cfg(feature = "replace")]
1211            ReplaceStrict { return_dtype } => {
1212                map_as_slice!(dispatch::replace_strict, return_dtype.clone())
1213            },
1214
1215            FillNullWithStrategy(strategy) => map!(dispatch::fill_null_with_strategy, strategy),
1216            GatherEvery { n, offset } => map!(dispatch::gather_every, n, offset),
1217            #[cfg(feature = "reinterpret")]
1218            Reinterpret(signed) => map!(dispatch::reinterpret, signed),
1219            ExtendConstant => map_as_slice!(dispatch::extend_constant),
1220        }
1221    }
1222}