1#[cfg(feature = "iejoin")]
2use polars::prelude::InequalityOperator;
3use polars::series::ops::NullBehavior;
4use polars_compute::rolling::{QuantileMethod, RollingFnParams};
5use polars_core::chunked_array::ops::FillNullStrategy;
6#[cfg(feature = "string_normalize")]
7use polars_ops::chunked_array::UnicodeForm;
8use polars_ops::prelude::RankMethod;
9#[cfg(feature = "search_sorted")]
10use polars_ops::series::SearchSortedSide;
11use polars_ops::series::{ClosedInterval, InterpolationMethod};
12use polars_plan::dsl::DateRangeArgs;
13use polars_plan::plans::{
14 DynListLiteralValue, DynLiteralValue, FusedOperator, IRArrayFunction, IRBitwiseFunction,
15 IRBooleanFunction, IRCorrelationMethod, IRFunctionExpr, IRListFunction, IRPowFunction,
16 IRRandomMethod, IRRangeFunction, IRRollingFunction, IRRollingFunctionBy, IRStringFunction,
17 IRStructFunction, IRTemporalFunction,
18};
19use polars_plan::prelude::{
20 AExpr, GroupbyOptions, IRAggExpr, LiteralValue, Operator, PlanCallback, WindowMapping,
21};
22use polars_time::prelude::RollingGroupOptions;
23use polars_time::{ClosedWindow, Duration, DynamicGroupOptions};
24use polars_utils::itertools::Itertools;
25use pyo3::IntoPyObjectExt;
26use pyo3::exceptions::PyNotImplementedError;
27use pyo3::prelude::*;
28use pyo3::types::{PyBytes, PyInt, PyList, PyTuple};
29
30use crate::Wrap;
31use crate::lazyframe::visit::PyExprIR;
32use crate::series::PySeries;
33
34#[pyclass(frozen)]
35pub struct Alias {
36 #[pyo3(get)]
37 expr: usize,
38 #[pyo3(get)]
39 name: Py<PyAny>,
40}
41
42#[pyclass(frozen)]
43pub struct Column {
44 #[pyo3(get)]
45 name: Py<PyAny>,
46}
47
48#[pyclass(frozen)]
49pub struct Literal {
50 #[pyo3(get)]
51 value: Py<PyAny>,
52 #[pyo3(get)]
53 dtype: Py<PyAny>,
54}
55
56#[pyclass(name = "Operator", eq, frozen, skip_from_py_object)]
57#[derive(Copy, Clone, PartialEq)]
58pub enum PyOperator {
59 Eq,
60 EqValidity,
61 NotEq,
62 NotEqValidity,
63 Lt,
64 LtEq,
65 Gt,
66 GtEq,
67 Plus,
68 Minus,
69 Multiply,
70 Divide,
71 TrueDivide,
72 FloorDivide,
73 Modulus,
74 And,
75 Or,
76 Xor,
77 LogicalAnd,
78 LogicalOr,
79}
80
81#[pymethods]
82impl PyOperator {
83 fn __hash__(&self) -> isize {
84 *self as isize
85 }
86}
87
88impl<'py> IntoPyObject<'py> for Wrap<Operator> {
89 type Target = PyOperator;
90 type Output = Bound<'py, Self::Target>;
91 type Error = PyErr;
92
93 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
94 match self.0 {
95 Operator::Eq => PyOperator::Eq,
96 Operator::EqValidity => PyOperator::EqValidity,
97 Operator::NotEq => PyOperator::NotEq,
98 Operator::NotEqValidity => PyOperator::NotEqValidity,
99 Operator::Lt => PyOperator::Lt,
100 Operator::LtEq => PyOperator::LtEq,
101 Operator::Gt => PyOperator::Gt,
102 Operator::GtEq => PyOperator::GtEq,
103 Operator::Plus => PyOperator::Plus,
104 Operator::Minus => PyOperator::Minus,
105 Operator::Multiply => PyOperator::Multiply,
106 Operator::RustDivide => PyOperator::Divide,
107 Operator::TrueDivide => PyOperator::TrueDivide,
108 Operator::FloorDivide => PyOperator::FloorDivide,
109 Operator::Modulus => PyOperator::Modulus,
110 Operator::And => PyOperator::And,
111 Operator::Or => PyOperator::Or,
112 Operator::Xor => PyOperator::Xor,
113 Operator::LogicalAnd => PyOperator::LogicalAnd,
114 Operator::LogicalOr => PyOperator::LogicalOr,
115 }
116 .into_pyobject(py)
117 }
118}
119
120#[cfg(feature = "iejoin")]
121impl<'py> IntoPyObject<'py> for Wrap<InequalityOperator> {
122 type Target = PyOperator;
123 type Output = Bound<'py, Self::Target>;
124 type Error = PyErr;
125
126 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
127 match self.0 {
128 InequalityOperator::Lt => PyOperator::Lt,
129 InequalityOperator::LtEq => PyOperator::LtEq,
130 InequalityOperator::Gt => PyOperator::Gt,
131 InequalityOperator::GtEq => PyOperator::GtEq,
132 }
133 .into_pyobject(py)
134 }
135}
136
137#[pyclass(name = "StringFunction", eq, frozen, skip_from_py_object)]
138#[derive(Copy, Clone, PartialEq)]
139pub enum PyStringFunction {
140 ConcatHorizontal,
141 ConcatVertical,
142 Contains,
143 CountMatches,
144 EndsWith,
145 Extract,
146 ExtractAll,
147 ExtractGroups,
148 Find,
149 ToInteger,
150 LenBytes,
151 LenChars,
152 Lowercase,
153 JsonDecode,
154 JsonPathMatch,
155 Replace,
156 Reverse,
157 PadStart,
158 PadEnd,
159 Slice,
160 Head,
161 Tail,
162 HexEncode,
163 HexDecode,
164 Base64Encode,
165 Base64Decode,
166 StartsWith,
167 StripChars,
168 StripCharsStart,
169 StripCharsEnd,
170 StripPrefix,
171 StripSuffix,
172 SplitExact,
173 SplitN,
174 Strptime,
175 Split,
176 SplitRegex,
177 ToDecimal,
178 Titlecase,
179 Uppercase,
180 ZFill,
181 ContainsAny,
182 ReplaceMany,
183 EscapeRegex,
184 Normalize,
185 Format,
186 ExtractMany,
187 FindMany,
188}
189
190#[pymethods]
191impl PyStringFunction {
192 fn __hash__(&self) -> isize {
193 *self as isize
194 }
195}
196
197#[pyclass(name = "BooleanFunction", eq, frozen, skip_from_py_object)]
198#[derive(Copy, Clone, PartialEq)]
199pub enum PyBooleanFunction {
200 Any,
201 All,
202 IsEmpty,
203 HasNulls,
204 IsNull,
205 IsNotNull,
206 IsFinite,
207 IsInfinite,
208 IsNan,
209 IsNotNan,
210 IsFirstDistinct,
211 IsLastDistinct,
212 IsUnique,
213 IsDuplicated,
214 IsBetween,
215 IsIn,
216 IsClose,
217 IsSorted,
218 AllHorizontal,
219 AnyHorizontal,
220 Not,
221}
222
223#[pymethods]
224impl PyBooleanFunction {
225 fn __hash__(&self) -> isize {
226 *self as isize
227 }
228}
229
230#[pyclass(name = "TemporalFunction", eq, frozen, skip_from_py_object)]
231#[derive(Copy, Clone, PartialEq)]
232pub enum PyTemporalFunction {
233 Millennium,
234 Century,
235 Year,
236 IsLeapYear,
237 IsoYear,
238 Quarter,
239 Month,
240 DaysInMonth,
241 Week,
242 WeekDay,
243 Day,
244 OrdinalDay,
245 Time,
246 Date,
247 Datetime,
248 Duration,
249 Hour,
250 Minute,
251 Second,
252 Millisecond,
253 Microsecond,
254 Nanosecond,
255 TotalDays,
256 TotalHours,
257 TotalMinutes,
258 TotalSeconds,
259 TotalMilliseconds,
260 TotalMicroseconds,
261 TotalNanoseconds,
262 ToString,
263 CastTimeUnit,
264 WithTimeUnit,
265 ConvertTimeZone,
266 TimeStamp,
267 Truncate,
268 OffsetBy,
269 MonthStart,
270 MonthEnd,
271 BaseUtcOffset,
272 DSTOffset,
273 Round,
274 Replace,
275 ReplaceTimeZone,
276 Combine,
277 DatetimeFunction,
278}
279
280#[pymethods]
281impl PyTemporalFunction {
282 fn __hash__(&self) -> isize {
283 *self as isize
284 }
285}
286
287#[pyclass(name = "StructFunction", eq, frozen, skip_from_py_object)]
288#[derive(Copy, Clone, PartialEq)]
289pub enum PyStructFunction {
290 FieldByName,
291 RenameFields,
292 DropFields,
293 PrefixFields,
294 SuffixFields,
295 JsonEncode,
296 WithFields,
297 MapFieldNames,
298}
299
300#[pymethods]
301impl PyStructFunction {
302 fn __hash__(&self) -> isize {
303 *self as isize
304 }
305}
306
307#[pyclass(name = "ListFunction", eq, frozen, skip_from_py_object)]
308#[derive(Copy, Clone, PartialEq)]
309pub enum PyListFunction {
310 Concat,
311 Contains,
312 DropNulls,
313 Get,
314 Length,
315 Sort,
316 SetOperation,
317 Sample,
318 Slice,
319 Shift,
320 Gather,
321 GatherEvery,
322 CountMatches,
323 Sum,
324 Max,
325 Min,
326 Mean,
327 Median,
328 Std,
329 Var,
330 ArgMin,
331 ArgMax,
332 Diff,
333 Join,
334 ToArray,
335 ToStruct,
336}
337
338#[pymethods]
339impl PyListFunction {
340 fn __hash__(&self) -> isize {
341 *self as isize
342 }
343}
344
345#[pyclass(name = "ArrayFunction", eq, frozen, skip_from_py_object)]
346#[derive(Copy, Clone, PartialEq)]
347pub enum PyArrayFunction {
348 Length,
349 Min,
350 Max,
351 Sum,
352 ToList,
353 Std,
354 Var,
355 Mean,
356 Median,
357 Sort,
358 ArgMin,
359 ArgMax,
360 Get,
361 Join,
362 Contains,
363 CountMatches,
364 Shift,
365 Explode,
366 Concat,
367 Slice,
368 ToStruct,
369}
370
371#[pymethods]
372impl PyArrayFunction {
373 fn __hash__(&self) -> isize {
374 *self as isize
375 }
376}
377
378#[pyclass(name = "RollingFunction", eq, frozen, skip_from_py_object)]
379#[derive(Copy, Clone, PartialEq)]
380pub enum PyRollingFunction {
381 Min,
382 Max,
383 Mean,
384 Sum,
385 Quantile,
386 Var,
387 Std,
388 Rank,
389 Skew,
390 Kurtosis,
391 CorrCov,
392}
393
394#[pymethods]
395impl PyRollingFunction {
396 fn __hash__(&self) -> isize {
397 *self as isize
398 }
399}
400
401#[pyclass(name = "EwmFunction", eq, frozen, skip_from_py_object)]
402#[derive(Copy, Clone, PartialEq)]
403pub enum PyEwmFunction {
404 Mean,
405 Sum,
406 Std,
407 Var,
408 MeanBy,
409 SumBy,
410}
411
412#[pymethods]
413impl PyEwmFunction {
414 fn __hash__(&self) -> isize {
415 *self as isize
416 }
417}
418
419#[pyclass(name = "RollingFunctionBy", eq, frozen, skip_from_py_object)]
420#[derive(Copy, Clone, PartialEq)]
421pub enum PyRollingFunctionBy {
422 MinBy,
423 MaxBy,
424 MeanBy,
425 SumBy,
426 QuantileBy,
427 VarBy,
428 StdBy,
429 RankBy,
430}
431
432#[pymethods]
433impl PyRollingFunctionBy {
434 fn __hash__(&self) -> isize {
435 *self as isize
436 }
437}
438
439#[pyclass(name = "BitwiseFunction", eq, frozen, skip_from_py_object)]
440#[derive(Copy, Clone, PartialEq)]
441pub enum PyBitwiseFunction {
442 CountOnes,
443 CountZeros,
444 LeadingOnes,
445 LeadingZeros,
446 TrailingOnes,
447 TrailingZeros,
448 And,
449 Or,
450 Xor,
451}
452
453#[pymethods]
454impl PyBitwiseFunction {
455 fn __hash__(&self) -> isize {
456 *self as isize
457 }
458}
459
460#[pyclass(name = "RangeFunction", eq, frozen, skip_from_py_object)]
461#[derive(Copy, Clone, PartialEq)]
462pub enum PyRangeFunction {
463 IntRange,
464 IntRanges,
465 LinearSpace,
466 LinearSpaces,
467 DateRange,
468 DateRanges,
469 DatetimeRange,
470 DatetimeRanges,
471 TimeRange,
472 TimeRanges,
473}
474
475#[pymethods]
476impl PyRangeFunction {
477 fn __hash__(&self) -> isize {
478 *self as isize
479 }
480}
481
482#[pyclass(frozen)]
483pub struct BinaryExpr {
484 #[pyo3(get)]
485 left: usize,
486 #[pyo3(get)]
487 op: Py<PyAny>,
488 #[pyo3(get)]
489 right: usize,
490}
491
492#[pyclass(frozen)]
493pub struct Cast {
494 #[pyo3(get)]
495 expr: usize,
496 #[pyo3(get)]
497 dtype: Py<PyAny>,
498 #[pyo3(get)]
502 options: u8,
503}
504
505#[pyclass(frozen)]
506pub struct Sort {
507 #[pyo3(get)]
508 expr: usize,
509 #[pyo3(get)]
510 options: (bool, bool, bool),
512}
513
514#[pyclass(frozen)]
515pub struct Gather {
516 #[pyo3(get)]
517 expr: usize,
518 #[pyo3(get)]
519 idx: usize,
520 #[pyo3(get)]
521 scalar: bool,
522}
523
524#[pyclass(frozen)]
525pub struct Filter {
526 #[pyo3(get)]
527 input: usize,
528 #[pyo3(get)]
529 by: usize,
530}
531
532#[pyclass(frozen)]
533pub struct SortBy {
534 #[pyo3(get)]
535 expr: usize,
536 #[pyo3(get)]
537 by: Vec<usize>,
538 #[pyo3(get)]
539 sort_options: (bool, Vec<bool>, Vec<bool>),
541}
542
543#[pyclass(frozen)]
544pub struct Agg {
545 #[pyo3(get)]
546 name: Py<PyAny>,
547 #[pyo3(get)]
548 arguments: Vec<usize>,
549 #[pyo3(get)]
550 options: Py<PyAny>,
552}
553
554#[pyclass(frozen)]
555pub struct Ternary {
556 #[pyo3(get)]
557 predicate: usize,
558 #[pyo3(get)]
559 truthy: usize,
560 #[pyo3(get)]
561 falsy: usize,
562}
563
564#[pyclass(frozen)]
565pub struct Function {
566 #[pyo3(get)]
567 input: Vec<usize>,
568 #[pyo3(get)]
569 function_data: Py<PyAny>,
570 #[pyo3(get)]
571 options: Py<PyAny>,
572}
573
574#[pyclass(frozen)]
575pub struct Slice {
576 #[pyo3(get)]
577 input: usize,
578 #[pyo3(get)]
579 offset: usize,
580 #[pyo3(get)]
581 length: usize,
582}
583
584#[pyclass(frozen)]
585pub struct Len {}
586
587#[pyclass(frozen)]
588pub struct StructEval {
589 #[pyo3(get)]
590 expr: usize,
591 #[pyo3(get)]
592 evaluation: Vec<PyExprIR>,
593}
594
595#[pyclass(frozen)]
596pub struct Explode {
597 #[pyo3(get)]
598 expr: usize,
599 #[pyo3(get)]
600 options: (bool, bool),
602}
603
604#[pyclass(frozen)]
605pub struct Window {
606 #[pyo3(get)]
607 function: usize,
608 #[pyo3(get)]
609 partition_by: Vec<usize>,
610 #[pyo3(get)]
611 order_by: Option<usize>,
612 #[pyo3(get)]
613 order_by_descending: bool,
614 #[pyo3(get)]
615 order_by_nulls_last: bool,
616 #[pyo3(get)]
617 options: Py<PyAny>,
618}
619
620#[pyclass(frozen)]
621pub struct Rolling {
622 #[pyo3(get)]
623 function: usize,
624 #[pyo3(get)]
625 index_column: usize,
626 #[pyo3(get)]
627 period: Py<PyAny>,
628 #[pyo3(get)]
629 offset: Py<PyAny>,
630 #[pyo3(get)]
631 closed_window: Py<PyAny>,
632}
633
634#[pyclass(name = "WindowMapping", frozen)]
635pub struct PyWindowMapping {
636 inner: WindowMapping,
637}
638
639#[pymethods]
640impl PyWindowMapping {
641 #[getter]
642 fn kind(&self) -> &str {
643 self.inner.into()
644 }
645}
646
647impl<'py> IntoPyObject<'py> for Wrap<Duration> {
648 type Target = PyTuple;
649 type Output = Bound<'py, Self::Target>;
650 type Error = PyErr;
651
652 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
653 (
654 self.0.months(),
655 self.0.weeks(),
656 self.0.days(),
657 self.0.nanoseconds(),
658 self.0.parsed_int,
659 self.0.negative(),
660 )
661 .into_pyobject(py)
662 }
663}
664
665impl<'py> IntoPyObject<'py> for Wrap<ClosedWindow> {
666 type Target = PyAny;
667 type Output = Bound<'py, Self::Target>;
668 type Error = PyErr;
669
670 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
671 let s = match self.0 {
672 ClosedWindow::Left => "left",
673 ClosedWindow::Right => "right",
674 ClosedWindow::Both => "both",
675 ClosedWindow::None => "none",
676 };
677 Ok(s.into_pyobject(py)?.into_any())
678 }
679}
680
681#[pyclass(name = "RollingGroupOptions", frozen)]
682pub struct PyRollingGroupOptions {
683 inner: RollingGroupOptions,
684}
685
686#[pymethods]
687impl PyRollingGroupOptions {
688 #[getter]
689 fn index_column(&self) -> &str {
690 self.inner.index_column.as_str()
691 }
692
693 #[getter]
694 fn period(&self) -> Wrap<Duration> {
695 Wrap(self.inner.period)
696 }
697
698 #[getter]
699 fn offset(&self) -> Wrap<Duration> {
700 Wrap(self.inner.offset)
701 }
702
703 #[getter]
704 fn closed_window(&self) -> &str {
705 self.inner.closed_window.into()
706 }
707}
708
709#[pyclass(name = "DynamicGroupOptions", frozen)]
710pub struct PyDynamicGroupOptions {
711 inner: DynamicGroupOptions,
712}
713
714#[pymethods]
715impl PyDynamicGroupOptions {
716 #[getter]
717 fn index_column(&self) -> &str {
718 self.inner.index_column.as_str()
719 }
720
721 #[getter]
722 fn every(&self) -> Wrap<Duration> {
723 Wrap(self.inner.every)
724 }
725
726 #[getter]
727 fn period(&self) -> Wrap<Duration> {
728 Wrap(self.inner.period)
729 }
730
731 #[getter]
732 fn offset(&self) -> Wrap<Duration> {
733 Wrap(self.inner.offset)
734 }
735
736 #[getter]
737 fn label(&self) -> &str {
738 self.inner.label.into()
739 }
740
741 #[getter]
742 fn include_boundaries(&self) -> bool {
743 self.inner.include_boundaries
744 }
745
746 #[getter]
747 fn closed_window(&self) -> &str {
748 self.inner.closed_window.into()
749 }
750 #[getter]
751 fn start_by(&self) -> &str {
752 self.inner.start_by.into()
753 }
754}
755
756#[pyclass(name = "GroupbyOptions", frozen)]
757pub struct PyGroupbyOptions {
758 inner: GroupbyOptions,
759}
760
761impl PyGroupbyOptions {
762 pub(crate) fn new(inner: GroupbyOptions) -> Self {
763 Self { inner }
764 }
765}
766
767#[pymethods]
768impl PyGroupbyOptions {
769 #[getter]
770 fn slice(&self) -> Option<(i64, usize)> {
771 self.inner.slice
772 }
773
774 #[getter]
775 fn dynamic(&self) -> Option<PyDynamicGroupOptions> {
776 self.inner
777 .dynamic
778 .as_ref()
779 .map(|f| PyDynamicGroupOptions { inner: f.clone() })
780 }
781
782 #[getter]
783 fn rolling(&self) -> Option<PyRollingGroupOptions> {
784 self.inner
785 .rolling
786 .as_ref()
787 .map(|f| PyRollingGroupOptions { inner: f.clone() })
788 }
789}
790
791fn rolling_fn_params_into_py(
794 py: Python<'_>,
795 fn_params: &Option<RollingFnParams>,
796) -> PyResult<Py<PyAny>> {
797 match fn_params {
798 None => ().into_py_any(py),
799 Some(RollingFnParams::Quantile(q)) => {
800 (q.prob, Into::<&str>::into(q.method)).into_py_any(py)
801 },
802 Some(RollingFnParams::Var(v)) => (v.ddof,).into_py_any(py),
803 Some(RollingFnParams::Rank { method, seed }) => {
804 let method = Into::<&str>::into(method);
805 (method, *seed).into_py_any(py)
806 },
807 Some(RollingFnParams::Skew { bias }) => (*bias,).into_py_any(py),
808 Some(RollingFnParams::Kurtosis { fisher, bias }) => (*fisher, *bias).into_py_any(py),
809 }
810}
811
812fn closed_interval_into_py(closed: &ClosedInterval) -> &'static str {
813 match closed {
814 ClosedInterval::Both => "both",
815 ClosedInterval::Left => "left",
816 ClosedInterval::Right => "right",
817 ClosedInterval::None => "none",
818 }
819}
820
821fn date_range_args_into_py(arg_type: &DateRangeArgs) -> &'static str {
822 match arg_type {
823 DateRangeArgs::StartEndInterval => "start_end_interval",
824 DateRangeArgs::StartEndSamples => "start_end_samples",
825 DateRangeArgs::StartIntervalSamples => "start_interval_samples",
826 DateRangeArgs::EndIntervalSamples => "end_interval_samples",
827 }
828}
829
830fn dyn_list_literal_into_py(py: Python<'_>, value: &DynListLiteralValue) -> PyResult<Py<PyAny>> {
832 match value {
833 DynListLiteralValue::Str(values) => values
834 .iter()
835 .map(|v| v.as_ref().map(|s| s.as_str()))
836 .collect::<Vec<_>>()
837 .into_py_any(py),
838 DynListLiteralValue::Int(values) => values.to_vec().into_py_any(py),
839 DynListLiteralValue::Float(values) => values.to_vec().into_py_any(py),
840 DynListLiteralValue::List(values) => {
841 let out = PyList::new(py, [] as [Bound<'_, PyAny>; 0])?;
842 for v in values.iter() {
843 match v {
844 None => out.append(py.None())?,
845 Some(inner) => out.append(dyn_list_literal_into_py(py, inner)?)?,
846 }
847 }
848 out.into_py_any(py)
849 },
850 }
851}
852
853pub(crate) fn into_py(py: Python<'_>, expr: &AExpr) -> PyResult<Py<PyAny>> {
854 match expr {
855 AExpr::Element => Err(PyNotImplementedError::new_err("element")),
856 AExpr::Explode { expr, options } => Explode {
857 expr: expr.0,
858 options: (options.empty_as_null, options.keep_nulls),
859 }
860 .into_py_any(py),
861 AExpr::Column(name) => Column {
862 name: name.into_py_any(py)?,
863 }
864 .into_py_any(py),
865 AExpr::StructField(_) => Err(PyNotImplementedError::new_err("field")),
866 AExpr::Literal(lit) => {
867 use polars_core::prelude::AnyValue;
868 let dtype: Py<PyAny> = Wrap(lit.get_datatype()).into_py_any(py)?;
869 let py_value = match lit {
870 LiteralValue::Dyn(d) => match d {
871 DynLiteralValue::Int(v) => v.into_py_any(py)?,
872 DynLiteralValue::Float(v) => v.into_py_any(py)?,
873 DynLiteralValue::Str(v) => v.into_py_any(py)?,
874 DynLiteralValue::List(v) => dyn_list_literal_into_py(py, v)?,
875 },
876 LiteralValue::Scalar(sc) => {
877 match sc.as_any_value() {
878 AnyValue::Duration(delta, _) => delta.into_py_any(py)?,
883 any => Wrap(any).into_py_any(py)?,
884 }
885 },
886 LiteralValue::Range(range) => (range.low, range.high).into_py_any(py)?,
887 LiteralValue::Series(s) => PySeries::new((**s).clone()).into_py_any(py)?,
888 };
889
890 Literal {
891 value: py_value,
892 dtype,
893 }
894 }
895 .into_py_any(py),
896 AExpr::BinaryExpr { left, op, right } => BinaryExpr {
897 left: left.0,
898 op: Wrap(*op).into_py_any(py)?,
899 right: right.0,
900 }
901 .into_py_any(py),
902 AExpr::Cast {
903 expr,
904 dtype,
905 options,
906 } => Cast {
907 expr: expr.0,
908 dtype: Wrap(dtype.clone()).into_py_any(py)?,
909 options: *options as u8,
910 }
911 .into_py_any(py),
912 AExpr::Sort { expr, options } => Sort {
913 expr: expr.0,
914 options: (
915 options.maintain_order,
916 options.nulls_last,
917 options.descending,
918 ),
919 }
920 .into_py_any(py),
921 AExpr::Gather {
922 expr,
923 idx,
924 returns_scalar,
925 null_on_oob: _,
926 } => Gather {
927 expr: expr.0,
928 idx: idx.0,
929 scalar: *returns_scalar,
930 }
931 .into_py_any(py),
932 AExpr::Filter { input, by } => Filter {
933 input: input.0,
934 by: by.0,
935 }
936 .into_py_any(py),
937 AExpr::SortBy {
938 expr,
939 by,
940 sort_options,
941 } => SortBy {
942 expr: expr.0,
943 by: by.iter().map(|n| n.0).collect(),
944 sort_options: (
945 sort_options.maintain_order,
946 sort_options.nulls_last.clone(),
947 sort_options.descending.clone(),
948 ),
949 }
950 .into_py_any(py),
951 AExpr::Agg(aggexpr) => match aggexpr {
952 IRAggExpr::Min {
953 input,
954 propagate_nans,
955 } => Agg {
956 name: "min".into_py_any(py)?,
957 arguments: vec![input.0],
958 options: propagate_nans.into_py_any(py)?,
959 },
960 IRAggExpr::Max {
961 input,
962 propagate_nans,
963 } => Agg {
964 name: "max".into_py_any(py)?,
965 arguments: vec![input.0],
966 options: propagate_nans.into_py_any(py)?,
967 },
968 IRAggExpr::Median(n) => Agg {
969 name: "median".into_py_any(py)?,
970 arguments: vec![n.0],
971 options: py.None(),
972 },
973 IRAggExpr::NUnique(n) => Agg {
974 name: "n_unique".into_py_any(py)?,
975 arguments: vec![n.0],
976 options: py.None(),
977 },
978 IRAggExpr::First(n) => Agg {
979 name: "first".into_py_any(py)?,
980 arguments: vec![n.0],
981 options: py.None(),
982 },
983 IRAggExpr::FirstNonNull(n) => Agg {
984 name: "first_non_null".into_py_any(py)?,
985 arguments: vec![n.0],
986 options: py.None(),
987 },
988 IRAggExpr::Last(n) => Agg {
989 name: "last".into_py_any(py)?,
990 arguments: vec![n.0],
991 options: py.None(),
992 },
993 IRAggExpr::LastNonNull(n) => Agg {
994 name: "last_non_null".into_py_any(py)?,
995 arguments: vec![n.0],
996 options: py.None(),
997 },
998 IRAggExpr::Item {
999 input: n,
1000 allow_empty,
1001 } => Agg {
1002 name: "item".into_py_any(py)?,
1003 arguments: vec![n.0],
1004 options: allow_empty.into_py_any(py)?,
1005 },
1006 IRAggExpr::Mean(n) => Agg {
1007 name: "mean".into_py_any(py)?,
1008 arguments: vec![n.0],
1009 options: py.None(),
1010 },
1011 IRAggExpr::Implode {
1012 input: n,
1013 maintain_order,
1014 } => Agg {
1015 name: "implode".into_py_any(py)?,
1016 arguments: vec![n.0],
1017 options: maintain_order.into_py_any(py)?,
1018 },
1019 IRAggExpr::Sum(n) => Agg {
1020 name: "sum".into_py_any(py)?,
1021 arguments: vec![n.0],
1022 options: py.None(),
1023 },
1024 IRAggExpr::Count {
1025 input: n,
1026 include_nulls,
1027 } => Agg {
1028 name: "count".into_py_any(py)?,
1029 arguments: vec![n.0],
1030 options: include_nulls.into_py_any(py)?,
1031 },
1032 IRAggExpr::Std(n, ddof) => Agg {
1033 name: "std".into_py_any(py)?,
1034 arguments: vec![n.0],
1035 options: ddof.into_py_any(py)?,
1036 },
1037 IRAggExpr::Var(n, ddof) => Agg {
1038 name: "var".into_py_any(py)?,
1039 arguments: vec![n.0],
1040 options: ddof.into_py_any(py)?,
1041 },
1042 IRAggExpr::AggGroups(n) => Agg {
1043 name: "agg_groups".into_py_any(py)?,
1044 arguments: vec![n.0],
1045 options: py.None(),
1046 },
1047 }
1048 .into_py_any(py),
1049 AExpr::Ternary {
1050 predicate,
1051 truthy,
1052 falsy,
1053 } => Ternary {
1054 predicate: predicate.0,
1055 truthy: truthy.0,
1056 falsy: falsy.0,
1057 }
1058 .into_py_any(py),
1059 AExpr::AnonymousFunction { .. } => Err(PyNotImplementedError::new_err("anonymousfunction")),
1060 AExpr::AnonymousAgg { .. } => {
1061 Err(PyNotImplementedError::new_err("anonymous_streaming_agg"))
1062 },
1063 AExpr::Function {
1064 input,
1065 function,
1066 options: _,
1068 } => Function {
1069 input: input.iter().map(|n| n.node().0).collect(),
1070 function_data: match function {
1071 IRFunctionExpr::ArrayExpr(f) => match f {
1072 IRArrayFunction::Length => (PyArrayFunction::Length,).into_py_any(py),
1073 IRArrayFunction::Min => (PyArrayFunction::Min,).into_py_any(py),
1074 IRArrayFunction::Max => (PyArrayFunction::Max,).into_py_any(py),
1075 IRArrayFunction::Sum => (PyArrayFunction::Sum,).into_py_any(py),
1076 IRArrayFunction::ToList => (PyArrayFunction::ToList,).into_py_any(py),
1077 IRArrayFunction::Std(ddof) => (PyArrayFunction::Std, *ddof).into_py_any(py),
1078 IRArrayFunction::Var(ddof) => (PyArrayFunction::Var, *ddof).into_py_any(py),
1079 IRArrayFunction::Mean => (PyArrayFunction::Mean,).into_py_any(py),
1080 IRArrayFunction::Median => (PyArrayFunction::Median,).into_py_any(py),
1081 IRArrayFunction::Sort(options) => (
1082 PyArrayFunction::Sort,
1083 options.descending,
1084 options.nulls_last,
1085 )
1086 .into_py_any(py),
1087 IRArrayFunction::ArgMin => (PyArrayFunction::ArgMin,).into_py_any(py),
1088 IRArrayFunction::ArgMax => (PyArrayFunction::ArgMax,).into_py_any(py),
1089 IRArrayFunction::Get(null_on_oob) => {
1090 (PyArrayFunction::Get, *null_on_oob).into_py_any(py)
1091 },
1092 IRArrayFunction::Join(ignore_nulls) => {
1093 (PyArrayFunction::Join, *ignore_nulls).into_py_any(py)
1094 },
1095 #[cfg(feature = "is_in")]
1096 IRArrayFunction::Contains { nulls_equal } => {
1097 (PyArrayFunction::Contains, *nulls_equal).into_py_any(py)
1098 },
1099 #[cfg(feature = "array_count")]
1100 IRArrayFunction::CountMatches => {
1101 (PyArrayFunction::CountMatches,).into_py_any(py)
1102 },
1103 IRArrayFunction::Shift => (PyArrayFunction::Shift,).into_py_any(py),
1104 IRArrayFunction::Explode(options) => (
1105 PyArrayFunction::Explode,
1106 options.empty_as_null,
1107 options.keep_nulls,
1108 )
1109 .into_py_any(py),
1110 IRArrayFunction::Concat => (PyArrayFunction::Concat,).into_py_any(py),
1111 IRArrayFunction::Slice(offset, length) => {
1112 (PyArrayFunction::Slice, *offset, *length).into_py_any(py)
1113 },
1114 IRArrayFunction::ToStruct(name_generator) => match name_generator {
1117 None => (PyArrayFunction::ToStruct, py.None()).into_py_any(py),
1118 Some(_) => {
1119 return Err(PyNotImplementedError::new_err(
1120 "array to_struct with a name generator",
1121 ));
1122 },
1123 },
1124 },
1125 IRFunctionExpr::BinaryExpr(_) => {
1126 return Err(PyNotImplementedError::new_err("binary expr"));
1127 },
1128 IRFunctionExpr::Categorical(_) => {
1129 return Err(PyNotImplementedError::new_err("categorical expr"));
1130 },
1131 IRFunctionExpr::Extension(_) => {
1132 return Err(PyNotImplementedError::new_err("extension expr"));
1133 },
1134 IRFunctionExpr::ListExpr(listfun) => match listfun {
1135 IRListFunction::Concat => (PyListFunction::Concat,).into_py_any(py),
1136 #[cfg(feature = "is_in")]
1137 IRListFunction::Contains { nulls_equal } => {
1138 (PyListFunction::Contains, nulls_equal).into_py_any(py)
1139 },
1140 #[cfg(feature = "list_drop_nulls")]
1141 IRListFunction::DropNulls => (PyListFunction::DropNulls,).into_py_any(py),
1142 IRListFunction::Get(null_on_oob) => {
1143 (PyListFunction::Get, null_on_oob).into_py_any(py)
1144 },
1145 IRListFunction::Length => (PyListFunction::Length,).into_py_any(py),
1146 IRListFunction::Sort(options) => {
1147 (PyListFunction::Sort, options.descending, options.nulls_last)
1148 .into_py_any(py)
1149 },
1150 #[cfg(feature = "list_sets")]
1151 IRListFunction::SetOperation(set_operation) => (
1152 PyListFunction::SetOperation,
1153 Into::<&str>::into(set_operation),
1154 )
1155 .into_py_any(py),
1156 #[cfg(feature = "list_sample")]
1157 IRListFunction::Sample {
1158 is_fraction,
1159 with_replacement,
1160 shuffle,
1161 seed,
1162 } => (
1163 PyListFunction::Sample,
1164 is_fraction,
1165 with_replacement,
1166 shuffle,
1167 seed,
1168 )
1169 .into_py_any(py),
1170 IRListFunction::Slice => (PyListFunction::Slice,).into_py_any(py),
1171 IRListFunction::Shift => (PyListFunction::Shift,).into_py_any(py),
1172 #[cfg(feature = "list_gather")]
1173 IRListFunction::Gather(null_on_oob) => {
1174 (PyListFunction::Gather, null_on_oob).into_py_any(py)
1175 },
1176 #[cfg(feature = "list_gather")]
1177 IRListFunction::GatherEvery => (PyListFunction::GatherEvery,).into_py_any(py),
1178 #[cfg(feature = "list_count")]
1179 IRListFunction::CountMatches => (PyListFunction::CountMatches,).into_py_any(py),
1180 IRListFunction::Sum => (PyListFunction::Sum,).into_py_any(py),
1181 IRListFunction::Max => (PyListFunction::Max,).into_py_any(py),
1182 IRListFunction::Min => (PyListFunction::Min,).into_py_any(py),
1183 IRListFunction::Mean => (PyListFunction::Mean,).into_py_any(py),
1184 IRListFunction::Median => (PyListFunction::Median,).into_py_any(py),
1185 IRListFunction::Std(ddof) => (PyListFunction::Std, ddof).into_py_any(py),
1186 IRListFunction::Var(ddof) => (PyListFunction::Var, ddof).into_py_any(py),
1187 IRListFunction::ArgMin => (PyListFunction::ArgMin,).into_py_any(py),
1188 IRListFunction::ArgMax => (PyListFunction::ArgMax,).into_py_any(py),
1189 IRListFunction::Diff { n, null_behavior } => (
1190 PyListFunction::Diff,
1191 n,
1192 match null_behavior {
1193 NullBehavior::Drop => "drop",
1194 NullBehavior::Ignore => "ignore",
1195 },
1196 )
1197 .into_py_any(py),
1198 IRListFunction::Join(ignore_nulls) => {
1199 (PyListFunction::Join, ignore_nulls).into_py_any(py)
1200 },
1201 #[cfg(feature = "dtype-array")]
1202 IRListFunction::ToArray(width) => {
1203 (PyListFunction::ToArray, width).into_py_any(py)
1204 },
1205 IRListFunction::ToStruct(names) => (
1206 PyListFunction::ToStruct,
1207 names.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
1208 )
1209 .into_py_any(py),
1210 },
1211 IRFunctionExpr::Bitwise(bitwisefun) => {
1212 let py_function = match bitwisefun {
1213 IRBitwiseFunction::CountOnes => PyBitwiseFunction::CountOnes,
1214 IRBitwiseFunction::CountZeros => PyBitwiseFunction::CountZeros,
1215 IRBitwiseFunction::LeadingOnes => PyBitwiseFunction::LeadingOnes,
1216 IRBitwiseFunction::LeadingZeros => PyBitwiseFunction::LeadingZeros,
1217 IRBitwiseFunction::TrailingOnes => PyBitwiseFunction::TrailingOnes,
1218 IRBitwiseFunction::TrailingZeros => PyBitwiseFunction::TrailingZeros,
1219 IRBitwiseFunction::And => PyBitwiseFunction::And,
1220 IRBitwiseFunction::Or => PyBitwiseFunction::Or,
1221 IRBitwiseFunction::Xor => PyBitwiseFunction::Xor,
1222 };
1223 (py_function,).into_py_any(py)
1224 },
1225 IRFunctionExpr::StringExpr(strfun) => match strfun {
1226 IRStringFunction::Format { format, insertions } => (
1227 PyStringFunction::Format,
1228 format.as_str(),
1229 insertions.to_vec(),
1230 )
1231 .into_py_any(py),
1232 IRStringFunction::ConcatHorizontal {
1233 delimiter,
1234 ignore_nulls,
1235 } => (
1236 PyStringFunction::ConcatHorizontal,
1237 delimiter.as_str(),
1238 ignore_nulls,
1239 )
1240 .into_py_any(py),
1241 IRStringFunction::ConcatVertical {
1242 delimiter,
1243 ignore_nulls,
1244 } => (
1245 PyStringFunction::ConcatVertical,
1246 delimiter.as_str(),
1247 ignore_nulls,
1248 )
1249 .into_py_any(py),
1250 #[cfg(feature = "regex")]
1251 IRStringFunction::Contains { literal, strict } => {
1252 (PyStringFunction::Contains, literal, strict).into_py_any(py)
1253 },
1254 IRStringFunction::CountMatches(literal) => {
1255 (PyStringFunction::CountMatches, literal).into_py_any(py)
1256 },
1257 IRStringFunction::EndsWith => (PyStringFunction::EndsWith,).into_py_any(py),
1258 IRStringFunction::Extract(group_index) => {
1259 (PyStringFunction::Extract, group_index).into_py_any(py)
1260 },
1261 IRStringFunction::ExtractAll => (PyStringFunction::ExtractAll,).into_py_any(py),
1262 #[cfg(feature = "extract_groups")]
1263 IRStringFunction::ExtractGroups { dtype, pat } => (
1264 PyStringFunction::ExtractGroups,
1265 &Wrap(dtype.clone()),
1266 pat.as_str(),
1267 )
1268 .into_py_any(py),
1269 #[cfg(feature = "regex")]
1270 IRStringFunction::Find { literal, strict } => {
1271 (PyStringFunction::Find, literal, strict).into_py_any(py)
1272 },
1273 IRStringFunction::ToInteger { dtype: _, strict } => {
1274 (PyStringFunction::ToInteger, strict).into_py_any(py)
1275 },
1276 IRStringFunction::LenBytes => (PyStringFunction::LenBytes,).into_py_any(py),
1277 IRStringFunction::LenChars => (PyStringFunction::LenChars,).into_py_any(py),
1278 IRStringFunction::Lowercase => (PyStringFunction::Lowercase,).into_py_any(py),
1279 #[cfg(feature = "extract_jsonpath")]
1280 IRStringFunction::JsonDecode(_) => {
1281 (PyStringFunction::JsonDecode, <Option<usize>>::None).into_py_any(py)
1282 },
1283 #[cfg(feature = "extract_jsonpath")]
1284 IRStringFunction::JsonPathMatch => {
1285 (PyStringFunction::JsonPathMatch,).into_py_any(py)
1286 },
1287 #[cfg(feature = "regex")]
1288 IRStringFunction::Replace { n, literal } => {
1289 (PyStringFunction::Replace, n, literal).into_py_any(py)
1290 },
1291 #[cfg(feature = "string_normalize")]
1292 IRStringFunction::Normalize { form } => (
1293 PyStringFunction::Normalize,
1294 match form {
1295 UnicodeForm::NFC => "nfc",
1296 UnicodeForm::NFKC => "nfkc",
1297 UnicodeForm::NFD => "nfd",
1298 UnicodeForm::NFKD => "nfkd",
1299 },
1300 )
1301 .into_py_any(py),
1302 IRStringFunction::Reverse => (PyStringFunction::Reverse,).into_py_any(py),
1303 IRStringFunction::PadStart { fill_char } => {
1304 (PyStringFunction::PadStart, fill_char).into_py_any(py)
1305 },
1306 IRStringFunction::PadEnd { fill_char } => {
1307 (PyStringFunction::PadEnd, fill_char).into_py_any(py)
1308 },
1309 IRStringFunction::Slice => (PyStringFunction::Slice,).into_py_any(py),
1310 IRStringFunction::Head => (PyStringFunction::Head,).into_py_any(py),
1311 IRStringFunction::Tail => (PyStringFunction::Tail,).into_py_any(py),
1312 IRStringFunction::HexEncode => (PyStringFunction::HexEncode,).into_py_any(py),
1313 #[cfg(feature = "binary_encoding")]
1314 IRStringFunction::HexDecode(strict) => {
1315 (PyStringFunction::HexDecode, strict).into_py_any(py)
1316 },
1317 IRStringFunction::Base64Encode => {
1318 (PyStringFunction::Base64Encode,).into_py_any(py)
1319 },
1320 #[cfg(feature = "binary_encoding")]
1321 IRStringFunction::Base64Decode(strict) => {
1322 (PyStringFunction::Base64Decode, strict).into_py_any(py)
1323 },
1324 IRStringFunction::StartsWith => (PyStringFunction::StartsWith,).into_py_any(py),
1325 IRStringFunction::StripChars => (PyStringFunction::StripChars,).into_py_any(py),
1326 IRStringFunction::StripCharsStart => {
1327 (PyStringFunction::StripCharsStart,).into_py_any(py)
1328 },
1329 IRStringFunction::StripCharsEnd => {
1330 (PyStringFunction::StripCharsEnd,).into_py_any(py)
1331 },
1332 IRStringFunction::StripPrefix => {
1333 (PyStringFunction::StripPrefix,).into_py_any(py)
1334 },
1335 IRStringFunction::StripSuffix => {
1336 (PyStringFunction::StripSuffix,).into_py_any(py)
1337 },
1338 IRStringFunction::SplitExact { n, inclusive } => {
1339 (PyStringFunction::SplitExact, n, inclusive).into_py_any(py)
1340 },
1341 IRStringFunction::SplitN(n) => (PyStringFunction::SplitN, n).into_py_any(py),
1342 IRStringFunction::Strptime(_, options) => (
1343 PyStringFunction::Strptime,
1344 options.format.as_ref().map(|s| s.as_str()),
1345 options.strict,
1346 options.exact,
1347 options.cache,
1348 )
1349 .into_py_any(py),
1350 IRStringFunction::Split(inclusive) => {
1351 (PyStringFunction::Split, inclusive).into_py_any(py)
1352 },
1353 IRStringFunction::SplitRegex { inclusive, strict } => {
1354 (PyStringFunction::SplitRegex, inclusive, strict).into_py_any(py)
1355 },
1356 IRStringFunction::ToDecimal { scale } => {
1357 (PyStringFunction::ToDecimal, scale).into_py_any(py)
1358 },
1359 #[cfg(feature = "nightly")]
1360 IRStringFunction::Titlecase => (PyStringFunction::Titlecase,).into_py_any(py),
1361 IRStringFunction::Uppercase => (PyStringFunction::Uppercase,).into_py_any(py),
1362 IRStringFunction::ZFill => (PyStringFunction::ZFill,).into_py_any(py),
1363 #[cfg(feature = "find_many")]
1364 IRStringFunction::ContainsAny {
1365 ascii_case_insensitive,
1366 } => (PyStringFunction::ContainsAny, ascii_case_insensitive).into_py_any(py),
1367 #[cfg(feature = "find_many")]
1368 IRStringFunction::ReplaceMany {
1369 ascii_case_insensitive,
1370 leftmost,
1371 } => (
1372 PyStringFunction::ReplaceMany,
1373 ascii_case_insensitive,
1374 leftmost,
1375 )
1376 .into_py_any(py),
1377 #[cfg(feature = "find_many")]
1378 IRStringFunction::ExtractMany {
1379 ascii_case_insensitive,
1380 overlapping,
1381 leftmost,
1382 } => (
1383 PyStringFunction::ExtractMany,
1384 ascii_case_insensitive,
1385 overlapping,
1386 leftmost,
1387 )
1388 .into_py_any(py),
1389 #[cfg(feature = "find_many")]
1390 IRStringFunction::FindMany {
1391 ascii_case_insensitive,
1392 overlapping,
1393 leftmost,
1394 } => (
1395 PyStringFunction::FindMany,
1396 ascii_case_insensitive,
1397 overlapping,
1398 leftmost,
1399 )
1400 .into_py_any(py),
1401 #[cfg(feature = "regex")]
1402 IRStringFunction::EscapeRegex => {
1403 (PyStringFunction::EscapeRegex,).into_py_any(py)
1404 },
1405 },
1406 IRFunctionExpr::StructExpr(fun) => match fun {
1407 IRStructFunction::FieldByName(name) => {
1408 (PyStructFunction::FieldByName, name.as_str()).into_py_any(py)
1409 },
1410 IRStructFunction::RenameFields(names) => (
1411 PyStructFunction::RenameFields,
1412 names.iter().map(|s| s.as_str()).collect_vec(),
1413 )
1414 .into_py_any(py),
1415 IRStructFunction::DropFields(names, strict) => (
1416 PyStructFunction::DropFields,
1417 names.iter().map(|s| s.as_str()).collect_vec(),
1418 strict,
1419 )
1420 .into_py_any(py),
1421 IRStructFunction::PrefixFields(prefix) => {
1422 (PyStructFunction::PrefixFields, prefix.as_str()).into_py_any(py)
1423 },
1424 IRStructFunction::SuffixFields(prefix) => {
1425 (PyStructFunction::SuffixFields, prefix.as_str()).into_py_any(py)
1426 },
1427 #[cfg(feature = "json")]
1428 IRStructFunction::JsonEncode => (PyStructFunction::JsonEncode,).into_py_any(py),
1429 IRStructFunction::MapFieldNames(function) => match function {
1430 PlanCallback::Python(lambda) => {
1431 (PyStructFunction::MapFieldNames, lambda.0.clone_ref(py))
1432 .into_py_any(py)
1433 },
1434 PlanCallback::Rust(_) => {
1435 return Err(PyNotImplementedError::new_err(
1436 "map_field_names with rust callback",
1437 ));
1438 },
1439 },
1440 },
1441 IRFunctionExpr::TemporalExpr(fun) => match fun {
1442 IRTemporalFunction::Millennium => {
1443 (PyTemporalFunction::Millennium,).into_py_any(py)
1444 },
1445 IRTemporalFunction::Century => (PyTemporalFunction::Century,).into_py_any(py),
1446 IRTemporalFunction::Year => (PyTemporalFunction::Year,).into_py_any(py),
1447 IRTemporalFunction::IsLeapYear => {
1448 (PyTemporalFunction::IsLeapYear,).into_py_any(py)
1449 },
1450 IRTemporalFunction::IsoYear => (PyTemporalFunction::IsoYear,).into_py_any(py),
1451 IRTemporalFunction::Quarter => (PyTemporalFunction::Quarter,).into_py_any(py),
1452 IRTemporalFunction::Month => (PyTemporalFunction::Month,).into_py_any(py),
1453 IRTemporalFunction::Week => (PyTemporalFunction::Week,).into_py_any(py),
1454 IRTemporalFunction::WeekDay => (PyTemporalFunction::WeekDay,).into_py_any(py),
1455 IRTemporalFunction::Day => (PyTemporalFunction::Day,).into_py_any(py),
1456 IRTemporalFunction::OrdinalDay => {
1457 (PyTemporalFunction::OrdinalDay,).into_py_any(py)
1458 },
1459 IRTemporalFunction::Time => (PyTemporalFunction::Time,).into_py_any(py),
1460 IRTemporalFunction::Date => (PyTemporalFunction::Date,).into_py_any(py),
1461 IRTemporalFunction::Datetime => (PyTemporalFunction::Datetime,).into_py_any(py),
1462 IRTemporalFunction::Duration(time_unit) => {
1463 (PyTemporalFunction::Duration, Wrap(*time_unit)).into_py_any(py)
1464 },
1465 IRTemporalFunction::Hour => (PyTemporalFunction::Hour,).into_py_any(py),
1466 IRTemporalFunction::Minute => (PyTemporalFunction::Minute,).into_py_any(py),
1467 IRTemporalFunction::Second => (PyTemporalFunction::Second,).into_py_any(py),
1468 IRTemporalFunction::Millisecond => {
1469 (PyTemporalFunction::Millisecond,).into_py_any(py)
1470 },
1471 IRTemporalFunction::Microsecond => {
1472 (PyTemporalFunction::Microsecond,).into_py_any(py)
1473 },
1474 IRTemporalFunction::Nanosecond => {
1475 (PyTemporalFunction::Nanosecond,).into_py_any(py)
1476 },
1477 IRTemporalFunction::DaysInMonth => {
1478 (PyTemporalFunction::DaysInMonth,).into_py_any(py)
1479 },
1480 IRTemporalFunction::TotalDays { fractional } => {
1481 (PyTemporalFunction::TotalDays, fractional).into_py_any(py)
1482 },
1483 IRTemporalFunction::TotalHours { fractional } => {
1484 (PyTemporalFunction::TotalHours, fractional).into_py_any(py)
1485 },
1486 IRTemporalFunction::TotalMinutes { fractional } => {
1487 (PyTemporalFunction::TotalMinutes, fractional).into_py_any(py)
1488 },
1489 IRTemporalFunction::TotalSeconds { fractional } => {
1490 (PyTemporalFunction::TotalSeconds, fractional).into_py_any(py)
1491 },
1492 IRTemporalFunction::TotalMilliseconds { fractional } => {
1493 (PyTemporalFunction::TotalMilliseconds, fractional).into_py_any(py)
1494 },
1495 IRTemporalFunction::TotalMicroseconds { fractional } => {
1496 (PyTemporalFunction::TotalMicroseconds, fractional).into_py_any(py)
1497 },
1498 IRTemporalFunction::TotalNanoseconds { fractional } => {
1499 (PyTemporalFunction::TotalNanoseconds, fractional).into_py_any(py)
1500 },
1501 IRTemporalFunction::ToString(format) => {
1502 (PyTemporalFunction::ToString, format).into_py_any(py)
1503 },
1504 IRTemporalFunction::CastTimeUnit(time_unit) => {
1505 (PyTemporalFunction::CastTimeUnit, Wrap(*time_unit)).into_py_any(py)
1506 },
1507 IRTemporalFunction::WithTimeUnit(time_unit) => {
1508 (PyTemporalFunction::WithTimeUnit, Wrap(*time_unit)).into_py_any(py)
1509 },
1510 #[cfg(feature = "timezones")]
1511 IRTemporalFunction::ConvertTimeZone(time_zone) => {
1512 (PyTemporalFunction::ConvertTimeZone, time_zone.as_str()).into_py_any(py)
1513 },
1514 IRTemporalFunction::TimeStamp(time_unit) => {
1515 (PyTemporalFunction::TimeStamp, Wrap(*time_unit)).into_py_any(py)
1516 },
1517 IRTemporalFunction::Truncate => (PyTemporalFunction::Truncate,).into_py_any(py),
1518 IRTemporalFunction::OffsetBy => (PyTemporalFunction::OffsetBy,).into_py_any(py),
1519 IRTemporalFunction::MonthStart => {
1520 (PyTemporalFunction::MonthStart,).into_py_any(py)
1521 },
1522 IRTemporalFunction::MonthEnd => (PyTemporalFunction::MonthEnd,).into_py_any(py),
1523 #[cfg(feature = "timezones")]
1524 IRTemporalFunction::BaseUtcOffset => {
1525 (PyTemporalFunction::BaseUtcOffset,).into_py_any(py)
1526 },
1527 #[cfg(feature = "timezones")]
1528 IRTemporalFunction::DSTOffset => {
1529 (PyTemporalFunction::DSTOffset,).into_py_any(py)
1530 },
1531 IRTemporalFunction::Round => (PyTemporalFunction::Round,).into_py_any(py),
1532 IRTemporalFunction::Replace => (PyTemporalFunction::Replace).into_py_any(py),
1533 #[cfg(feature = "timezones")]
1534 IRTemporalFunction::ReplaceTimeZone(time_zone, non_existent) => (
1535 PyTemporalFunction::ReplaceTimeZone,
1536 time_zone.as_ref().map(|s| s.as_str()),
1537 Into::<&str>::into(non_existent),
1538 )
1539 .into_py_any(py),
1540 IRTemporalFunction::Combine(time_unit) => {
1541 (PyTemporalFunction::Combine, Wrap(*time_unit)).into_py_any(py)
1542 },
1543 IRTemporalFunction::DatetimeFunction {
1544 time_unit,
1545 time_zone,
1546 } => (
1547 PyTemporalFunction::DatetimeFunction,
1548 Wrap(*time_unit),
1549 time_zone.as_ref().map(|s| s.as_str()),
1550 )
1551 .into_py_any(py),
1552 },
1553 IRFunctionExpr::Boolean(boolfun) => match boolfun {
1554 IRBooleanFunction::Any { ignore_nulls } => {
1555 (PyBooleanFunction::Any, *ignore_nulls).into_py_any(py)
1556 },
1557 IRBooleanFunction::All { ignore_nulls } => {
1558 (PyBooleanFunction::All, *ignore_nulls).into_py_any(py)
1559 },
1560 IRBooleanFunction::IsEmpty { ignore_nulls } => {
1561 (PyBooleanFunction::IsEmpty, *ignore_nulls).into_py_any(py)
1562 },
1563 IRBooleanFunction::HasNulls => (PyBooleanFunction::HasNulls,).into_py_any(py),
1564 IRBooleanFunction::IsNull => (PyBooleanFunction::IsNull,).into_py_any(py),
1565 IRBooleanFunction::IsNotNull => (PyBooleanFunction::IsNotNull,).into_py_any(py),
1566 IRBooleanFunction::IsFinite => (PyBooleanFunction::IsFinite,).into_py_any(py),
1567 IRBooleanFunction::IsInfinite => {
1568 (PyBooleanFunction::IsInfinite,).into_py_any(py)
1569 },
1570 IRBooleanFunction::IsNan => (PyBooleanFunction::IsNan,).into_py_any(py),
1571 IRBooleanFunction::IsNotNan => (PyBooleanFunction::IsNotNan,).into_py_any(py),
1572 IRBooleanFunction::IsFirstDistinct => {
1573 (PyBooleanFunction::IsFirstDistinct,).into_py_any(py)
1574 },
1575 IRBooleanFunction::IsLastDistinct => {
1576 (PyBooleanFunction::IsLastDistinct,).into_py_any(py)
1577 },
1578 IRBooleanFunction::IsUnique => (PyBooleanFunction::IsUnique,).into_py_any(py),
1579 IRBooleanFunction::IsDuplicated => {
1580 (PyBooleanFunction::IsDuplicated,).into_py_any(py)
1581 },
1582 IRBooleanFunction::IsBetween { closed } => {
1583 (PyBooleanFunction::IsBetween, Into::<&str>::into(closed)).into_py_any(py)
1584 },
1585 #[cfg(feature = "is_in")]
1586 IRBooleanFunction::IsIn { nulls_equal } => {
1587 (PyBooleanFunction::IsIn, nulls_equal).into_py_any(py)
1588 },
1589 IRBooleanFunction::IsClose {
1590 abs_tol,
1591 rel_tol,
1592 nans_equal,
1593 } => (PyBooleanFunction::IsClose, abs_tol.0, rel_tol.0, nans_equal)
1594 .into_py_any(py),
1595 IRBooleanFunction::IsSorted {
1596 descending,
1597 nulls_last,
1598 } => (PyBooleanFunction::IsSorted, *descending, *nulls_last).into_py_any(py),
1599 IRBooleanFunction::AllHorizontal => {
1600 (PyBooleanFunction::AllHorizontal,).into_py_any(py)
1601 },
1602 IRBooleanFunction::AnyHorizontal => {
1603 (PyBooleanFunction::AnyHorizontal,).into_py_any(py)
1604 },
1605 IRBooleanFunction::Not => (PyBooleanFunction::Not,).into_py_any(py),
1606 },
1607 IRFunctionExpr::Abs => ("abs",).into_py_any(py),
1608 #[cfg(feature = "hist")]
1609 IRFunctionExpr::Hist {
1610 bin_count,
1611 include_category,
1612 include_breakpoint,
1613 } => ("hist", bin_count, include_category, include_breakpoint).into_py_any(py),
1614 IRFunctionExpr::NullCount => ("null_count",).into_py_any(py),
1615 IRFunctionExpr::Pow(f) => match f {
1616 IRPowFunction::Generic => ("pow",).into_py_any(py),
1617 IRPowFunction::Sqrt => ("sqrt",).into_py_any(py),
1618 IRPowFunction::Cbrt => ("cbrt",).into_py_any(py),
1619 },
1620 IRFunctionExpr::Hash(seed, seed_1, seed_2, seed_3) => {
1621 ("hash", seed, seed_1, seed_2, seed_3).into_py_any(py)
1622 },
1623 IRFunctionExpr::ArgWhere => ("argwhere",).into_py_any(py),
1624 #[cfg(feature = "index_of")]
1625 IRFunctionExpr::IndexOf => ("index_of",).into_py_any(py),
1626 #[cfg(feature = "search_sorted")]
1627 IRFunctionExpr::SearchSorted { side, descending } => (
1628 "search_sorted",
1629 match side {
1630 SearchSortedSide::Any => "any",
1631 SearchSortedSide::Left => "left",
1632 SearchSortedSide::Right => "right",
1633 },
1634 descending,
1635 )
1636 .into_py_any(py),
1637 IRFunctionExpr::Range(rangefun) => match rangefun {
1638 IRRangeFunction::IntRange { step, dtype } => {
1639 (PyRangeFunction::IntRange, step, &Wrap(dtype.clone())).into_py_any(py)
1640 },
1641 IRRangeFunction::IntRanges { dtype } => {
1642 (PyRangeFunction::IntRanges, &Wrap(dtype.clone())).into_py_any(py)
1643 },
1644 IRRangeFunction::LinearSpace { closed } => (
1645 PyRangeFunction::LinearSpace,
1646 closed_interval_into_py(closed),
1647 )
1648 .into_py_any(py),
1649 IRRangeFunction::LinearSpaces {
1650 closed,
1651 array_width,
1652 } => (
1653 PyRangeFunction::LinearSpaces,
1654 closed_interval_into_py(closed),
1655 array_width,
1656 )
1657 .into_py_any(py),
1658 IRRangeFunction::DateRange {
1659 interval,
1660 closed,
1661 arg_type,
1662 } => (
1663 PyRangeFunction::DateRange,
1664 interval
1665 .as_ref()
1666 .map_or_else(|| Ok(py.None()), |d| Wrap(*d).into_py_any(py))?,
1667 Wrap(*closed).into_py_any(py)?,
1668 date_range_args_into_py(arg_type),
1669 )
1670 .into_py_any(py),
1671 IRRangeFunction::DateRanges {
1672 interval,
1673 closed,
1674 arg_type,
1675 } => (
1676 PyRangeFunction::DateRanges,
1677 interval
1678 .as_ref()
1679 .map_or_else(|| Ok(py.None()), |d| Wrap(*d).into_py_any(py))?,
1680 Wrap(*closed).into_py_any(py)?,
1681 date_range_args_into_py(arg_type),
1682 )
1683 .into_py_any(py),
1684 IRRangeFunction::DatetimeRange {
1685 interval,
1686 closed,
1687 time_unit,
1688 time_zone,
1689 arg_type,
1690 } => (
1691 PyRangeFunction::DatetimeRange,
1692 interval
1693 .as_ref()
1694 .map_or_else(|| Ok(py.None()), |d| Wrap(*d).into_py_any(py))?,
1695 Wrap(*closed).into_py_any(py)?,
1696 time_unit.map_or_else(|| Ok(py.None()), |tu| Wrap(tu).into_py_any(py))?,
1697 time_zone.as_ref().map(|s| s.as_str()),
1698 date_range_args_into_py(arg_type),
1699 )
1700 .into_py_any(py),
1701 IRRangeFunction::DatetimeRanges {
1702 interval,
1703 closed,
1704 time_unit,
1705 time_zone,
1706 arg_type,
1707 } => (
1708 PyRangeFunction::DatetimeRanges,
1709 interval
1710 .as_ref()
1711 .map_or_else(|| Ok(py.None()), |d| Wrap(*d).into_py_any(py))?,
1712 Wrap(*closed).into_py_any(py)?,
1713 time_unit.map_or_else(|| Ok(py.None()), |tu| Wrap(tu).into_py_any(py))?,
1714 time_zone.as_ref().map(|s| s.as_str()),
1715 date_range_args_into_py(arg_type),
1716 )
1717 .into_py_any(py),
1718 IRRangeFunction::TimeRange { interval, closed } => (
1719 PyRangeFunction::TimeRange,
1720 Wrap(*interval).into_py_any(py)?,
1721 Wrap(*closed).into_py_any(py)?,
1722 )
1723 .into_py_any(py),
1724 IRRangeFunction::TimeRanges { interval, closed } => (
1725 PyRangeFunction::TimeRanges,
1726 Wrap(*interval).into_py_any(py)?,
1727 Wrap(*closed).into_py_any(py)?,
1728 )
1729 .into_py_any(py),
1730 },
1731 #[cfg(feature = "trigonometry")]
1732 IRFunctionExpr::Trigonometry(trigfun) => {
1733 use polars_plan::plans::IRTrigonometricFunction;
1734
1735 match trigfun {
1736 IRTrigonometricFunction::Cos => ("cos",),
1737 IRTrigonometricFunction::Cot => ("cot",),
1738 IRTrigonometricFunction::Sin => ("sin",),
1739 IRTrigonometricFunction::Tan => ("tan",),
1740 IRTrigonometricFunction::ArcCos => ("arccos",),
1741 IRTrigonometricFunction::ArcSin => ("arcsin",),
1742 IRTrigonometricFunction::ArcTan => ("arctan",),
1743 IRTrigonometricFunction::Cosh => ("cosh",),
1744 IRTrigonometricFunction::Sinh => ("sinh",),
1745 IRTrigonometricFunction::Tanh => ("tanh",),
1746 IRTrigonometricFunction::ArcCosh => ("arccosh",),
1747 IRTrigonometricFunction::ArcSinh => ("arcsinh",),
1748 IRTrigonometricFunction::ArcTanh => ("arctanh",),
1749 IRTrigonometricFunction::Degrees => ("degrees",),
1750 IRTrigonometricFunction::Radians => ("radians",),
1751 }
1752 .into_py_any(py)
1753 },
1754 #[cfg(feature = "trigonometry")]
1755 IRFunctionExpr::Atan2 => ("atan2",).into_py_any(py),
1756 #[cfg(feature = "sign")]
1757 IRFunctionExpr::Sign => ("sign",).into_py_any(py),
1758 IRFunctionExpr::FillNull => ("fill_null",).into_py_any(py),
1759 IRFunctionExpr::RollingExpr { function, options } => match function {
1760 IRRollingFunction::CorrCov {
1761 corr_cov_options,
1762 is_corr,
1763 } => {
1764 (
1767 PyRollingFunction::CorrCov,
1768 corr_cov_options.window_size,
1769 corr_cov_options.min_periods,
1770 corr_cov_options.ddof,
1771 is_corr,
1772 )
1773 .into_py_any(py)
1774 },
1775 IRRollingFunction::Map(_) => {
1776 return Err(PyNotImplementedError::new_err("rolling map"));
1777 },
1778 _ => {
1779 let py_function = match function {
1780 IRRollingFunction::Min => PyRollingFunction::Min,
1781 IRRollingFunction::Max => PyRollingFunction::Max,
1782 IRRollingFunction::Mean => PyRollingFunction::Mean,
1783 IRRollingFunction::Sum => PyRollingFunction::Sum,
1784 IRRollingFunction::Quantile => PyRollingFunction::Quantile,
1785 IRRollingFunction::Var => PyRollingFunction::Var,
1786 IRRollingFunction::Std => PyRollingFunction::Std,
1787 IRRollingFunction::Rank => PyRollingFunction::Rank,
1788 IRRollingFunction::Skew => PyRollingFunction::Skew,
1789 IRRollingFunction::Kurtosis => PyRollingFunction::Kurtosis,
1790 IRRollingFunction::CorrCov { .. } | IRRollingFunction::Map(_) => {
1791 unreachable!()
1792 },
1793 };
1794 let fn_params = rolling_fn_params_into_py(py, &options.fn_params)?;
1795 (
1798 py_function,
1799 options.window_size,
1800 options.min_periods,
1801 &options.weights,
1802 options.center,
1803 fn_params,
1804 )
1805 .into_py_any(py)
1806 },
1807 },
1808 IRFunctionExpr::RollingExprBy {
1809 function_by,
1810 options,
1811 } => {
1812 let py_function = match function_by {
1813 IRRollingFunctionBy::MinBy => PyRollingFunctionBy::MinBy,
1814 IRRollingFunctionBy::MaxBy => PyRollingFunctionBy::MaxBy,
1815 IRRollingFunctionBy::MeanBy => PyRollingFunctionBy::MeanBy,
1816 IRRollingFunctionBy::SumBy => PyRollingFunctionBy::SumBy,
1817 IRRollingFunctionBy::QuantileBy => PyRollingFunctionBy::QuantileBy,
1818 IRRollingFunctionBy::VarBy => PyRollingFunctionBy::VarBy,
1819 IRRollingFunctionBy::StdBy => PyRollingFunctionBy::StdBy,
1820 IRRollingFunctionBy::RankBy => PyRollingFunctionBy::RankBy,
1821 };
1822 let fn_params = rolling_fn_params_into_py(py, &options.fn_params)?;
1823 (
1826 py_function,
1827 Wrap(options.window_size),
1828 options.min_periods,
1829 Wrap(options.closed_window),
1830 fn_params,
1831 )
1832 .into_py_any(py)
1833 },
1834 IRFunctionExpr::Rechunk => ("rechunk",).into_py_any(py),
1835 IRFunctionExpr::ShiftAndFill => ("shift_and_fill",).into_py_any(py),
1836 IRFunctionExpr::Shift => ("shift",).into_py_any(py),
1837 IRFunctionExpr::DropNans => ("drop_nans",).into_py_any(py),
1838 IRFunctionExpr::DropNulls => ("drop_nulls",).into_py_any(py),
1839 IRFunctionExpr::Quantile { method } => {
1840 let method = match method {
1841 QuantileMethod::Nearest => "nearest",
1842 QuantileMethod::Lower => "lower",
1843 QuantileMethod::Higher => "higher",
1844 QuantileMethod::Midpoint => "midpoint",
1845 QuantileMethod::Linear => "linear",
1846 QuantileMethod::Equiprobable => "equiprobable",
1847 };
1848 ("quantile", method).into_py_any(py)
1849 },
1850 IRFunctionExpr::Mode { maintain_order } => {
1851 ("mode", *maintain_order).into_py_any(py)
1852 },
1853 IRFunctionExpr::Skew(bias) => ("skew", bias).into_py_any(py),
1854 IRFunctionExpr::Kurtosis(fisher, bias) => {
1855 ("kurtosis", fisher, bias).into_py_any(py)
1856 },
1857 IRFunctionExpr::Reshape(_) => {
1858 return Err(PyNotImplementedError::new_err("reshape"));
1859 },
1860 #[cfg(feature = "repeat_by")]
1861 IRFunctionExpr::RepeatBy => ("repeat_by",).into_py_any(py),
1862 IRFunctionExpr::ArgUnique => ("arg_unique",).into_py_any(py),
1863 IRFunctionExpr::ArgMin => ("arg_min",).into_py_any(py),
1864 IRFunctionExpr::ArgMax => ("arg_max",).into_py_any(py),
1865 IRFunctionExpr::MinBy => ("min_by",).into_py_any(py),
1866 IRFunctionExpr::MaxBy => ("max_by",).into_py_any(py),
1867 IRFunctionExpr::ArgSort {
1868 descending,
1869 nulls_last,
1870 } => ("arg_max", descending, nulls_last).into_py_any(py),
1871 IRFunctionExpr::Product => ("product",).into_py_any(py),
1872 IRFunctionExpr::Repeat => ("repeat",).into_py_any(py),
1873 IRFunctionExpr::Rank { options, seed } => {
1874 let method = match options.method {
1875 RankMethod::Average => "average",
1876 RankMethod::Min => "min",
1877 RankMethod::Max => "max",
1878 RankMethod::Dense => "dense",
1879 RankMethod::Ordinal => "ordinal",
1880 RankMethod::Random => "random",
1881 };
1882 ("rank", method, options.descending, seed.map(|s| s as i64)).into_py_any(py)
1883 },
1884 IRFunctionExpr::Clip { has_min, has_max } => {
1885 ("clip", has_min, has_max).into_py_any(py)
1886 },
1887 IRFunctionExpr::AsList => ("as_list",).into_py_any(py),
1888 IRFunctionExpr::AsStruct => ("as_struct",).into_py_any(py),
1889 #[cfg(feature = "top_k")]
1890 IRFunctionExpr::TopK { descending } => ("top_k", descending).into_py_any(py),
1891 IRFunctionExpr::CumCount { reverse } => ("cum_count", reverse).into_py_any(py),
1892 IRFunctionExpr::CumSum { reverse } => ("cum_sum", reverse).into_py_any(py),
1893 IRFunctionExpr::CumProd { reverse } => ("cum_prod", reverse).into_py_any(py),
1894 IRFunctionExpr::CumMin { reverse } => ("cum_min", reverse).into_py_any(py),
1895 IRFunctionExpr::CumMax { reverse } => ("cum_max", reverse).into_py_any(py),
1896 IRFunctionExpr::Reverse => ("reverse",).into_py_any(py),
1897 IRFunctionExpr::ValueCounts {
1898 sort,
1899 parallel,
1900 name,
1901 normalize,
1902 } => ("value_counts", sort, parallel, name.as_str(), normalize).into_py_any(py),
1903 IRFunctionExpr::UniqueCounts => ("unique_counts",).into_py_any(py),
1904 IRFunctionExpr::ApproxNUnique => ("approx_n_unique",).into_py_any(py),
1905 IRFunctionExpr::Coalesce => ("coalesce",).into_py_any(py),
1906 IRFunctionExpr::Diff(null_behaviour) => (
1907 "diff",
1908 match null_behaviour {
1909 NullBehavior::Drop => "drop",
1910 NullBehavior::Ignore => "ignore",
1911 },
1912 )
1913 .into_py_any(py),
1914 #[cfg(feature = "pct_change")]
1915 IRFunctionExpr::PctChange => ("pct_change",).into_py_any(py),
1916 IRFunctionExpr::Interpolate(method) => (
1917 "interpolate",
1918 match method {
1919 InterpolationMethod::Linear => "linear",
1920 InterpolationMethod::Nearest => "nearest",
1921 },
1922 )
1923 .into_py_any(py),
1924 IRFunctionExpr::InterpolateBy => ("interpolate_by",).into_py_any(py),
1925 IRFunctionExpr::Entropy { base, normalize } => {
1926 ("entropy", base, normalize).into_py_any(py)
1927 },
1928 IRFunctionExpr::Log => ("log",).into_py_any(py),
1929 IRFunctionExpr::Log1p => ("log1p",).into_py_any(py),
1930 IRFunctionExpr::Exp => ("exp",).into_py_any(py),
1931 IRFunctionExpr::Unique(maintain_order) => {
1932 ("unique", maintain_order).into_py_any(py)
1933 },
1934 IRFunctionExpr::Round { decimals, mode } => {
1935 ("round", decimals, Into::<&str>::into(mode)).into_py_any(py)
1936 },
1937 IRFunctionExpr::RoundSF { digits } => ("round_sig_figs", digits).into_py_any(py),
1938 IRFunctionExpr::Truncate { decimals } => ("truncate", decimals).into_py_any(py),
1939 IRFunctionExpr::Floor => ("floor",).into_py_any(py),
1940 IRFunctionExpr::Ceil => ("ceil",).into_py_any(py),
1941 IRFunctionExpr::Fused(op) => {
1942 let op_name = match op {
1943 FusedOperator::MultiplyAdd => "fma",
1944 FusedOperator::SubMultiply => "fsm",
1945 FusedOperator::MultiplySub => "fms",
1946 };
1947 ("fused", op_name).into_py_any(py)
1948 },
1949 IRFunctionExpr::ConcatExpr { rechunk } => ("concat", rechunk).into_py_any(py),
1950 IRFunctionExpr::Correlation { method } => match method {
1951 IRCorrelationMethod::Pearson => ("corr", "pearson").into_py_any(py),
1952 IRCorrelationMethod::SpearmanRank(propagate_nans) => {
1953 ("corr", "spearman_rank", propagate_nans).into_py_any(py)
1954 },
1955 IRCorrelationMethod::Covariance(ddof) => {
1956 ("corr", "covariance", ddof).into_py_any(py)
1957 },
1958 },
1959 #[cfg(feature = "peaks")]
1960 IRFunctionExpr::PeakMin => ("peak_max",).into_py_any(py),
1961 #[cfg(feature = "peaks")]
1962 IRFunctionExpr::PeakMax => ("peak_min",).into_py_any(py),
1963 #[cfg(feature = "cutqcut")]
1964 IRFunctionExpr::Cut {
1965 breaks,
1966 labels,
1967 left_closed,
1968 include_breaks,
1969 } => (
1970 "cut",
1971 breaks,
1972 labels
1973 .as_ref()
1974 .map(|l| l.iter().map(|s| s.as_str()).collect::<Vec<_>>()),
1975 left_closed,
1976 include_breaks,
1977 )
1978 .into_py_any(py),
1979 #[cfg(feature = "cutqcut")]
1980 IRFunctionExpr::QCut {
1981 probs,
1982 labels,
1983 left_closed,
1984 allow_duplicates,
1985 include_breaks,
1986 } => (
1987 "qcut",
1988 probs,
1989 labels
1990 .as_ref()
1991 .map(|l| l.iter().map(|s| s.as_str()).collect::<Vec<_>>()),
1992 left_closed,
1993 allow_duplicates,
1994 include_breaks,
1995 )
1996 .into_py_any(py),
1997 #[cfg(feature = "rle")]
1998 IRFunctionExpr::RLE => ("rle",).into_py_any(py),
1999 #[cfg(feature = "rle")]
2000 IRFunctionExpr::RLEID => ("rle_id",).into_py_any(py),
2001 IRFunctionExpr::ToPhysical => ("to_physical",).into_py_any(py),
2002 IRFunctionExpr::Random { method, seed } => match method {
2003 IRRandomMethod::Shuffle => ("shuffle", seed).into_py_any(py),
2004 IRRandomMethod::Sample {
2005 is_fraction,
2006 with_replacement,
2007 shuffle,
2008 } => ("sample", is_fraction, with_replacement, shuffle, seed).into_py_any(py),
2009 },
2010 IRFunctionExpr::SetSortedFlag(sorted) => {
2011 ("set_sorted", sorted.descending, sorted.nulls_last).into_py_any(py)
2012 },
2013 #[cfg(feature = "ffi_plugin")]
2014 IRFunctionExpr::FfiPlugin {
2015 flags,
2016 lib,
2017 symbol,
2018 kwargs,
2019 } => (
2020 "ffi_plugin",
2021 lib.as_str(),
2022 symbol.as_str(),
2023 PyBytes::new(py, kwargs.as_ref()),
2024 flags.is_elementwise(),
2025 )
2026 .into_py_any(py),
2027 IRFunctionExpr::FoldHorizontal { .. } => {
2028 Err(PyNotImplementedError::new_err("fold"))
2029 },
2030 IRFunctionExpr::ReduceHorizontal { .. } => {
2031 Err(PyNotImplementedError::new_err("reduce"))
2032 },
2033 IRFunctionExpr::CumReduceHorizontal { .. } => {
2034 Err(PyNotImplementedError::new_err("cum_reduce"))
2035 },
2036 IRFunctionExpr::CumFoldHorizontal { .. } => {
2037 Err(PyNotImplementedError::new_err("cum_fold"))
2038 },
2039 IRFunctionExpr::SumHorizontal { ignore_nulls } => {
2040 ("sum_horizontal", ignore_nulls).into_py_any(py)
2041 },
2042 IRFunctionExpr::MaxHorizontal => ("max_horizontal",).into_py_any(py),
2043 IRFunctionExpr::MeanHorizontal { ignore_nulls } => {
2044 ("mean_horizontal", ignore_nulls).into_py_any(py)
2045 },
2046 IRFunctionExpr::MinHorizontal => ("min_horizontal",).into_py_any(py),
2047 IRFunctionExpr::EwmMean { options } => (
2051 PyEwmFunction::Mean,
2052 options.alpha,
2053 options.adjust,
2054 options.bias,
2055 options.min_periods,
2056 options.ignore_nulls,
2057 )
2058 .into_py_any(py),
2059 IRFunctionExpr::EwmSum { options } => (
2060 PyEwmFunction::Sum,
2061 options.alpha,
2062 options.min_periods,
2063 options.ignore_nulls,
2064 )
2065 .into_py_any(py),
2066 IRFunctionExpr::EwmStd { options } => (
2067 PyEwmFunction::Std,
2068 options.alpha,
2069 options.adjust,
2070 options.bias,
2071 options.min_periods,
2072 options.ignore_nulls,
2073 )
2074 .into_py_any(py),
2075 IRFunctionExpr::EwmVar { options } => (
2076 PyEwmFunction::Var,
2077 options.alpha,
2078 options.adjust,
2079 options.bias,
2080 options.min_periods,
2081 options.ignore_nulls,
2082 )
2083 .into_py_any(py),
2084 IRFunctionExpr::Replace => ("replace",).into_py_any(py),
2085 IRFunctionExpr::ReplaceStrict { return_dtype: _ } => {
2086 ("replace_strict",).into_py_any(py)
2088 },
2089 IRFunctionExpr::Negate => ("negate",).into_py_any(py),
2090 IRFunctionExpr::FillNullWithStrategy(strategy) => {
2091 let (strategy_str, py_limit): (&str, Py<PyAny>) = match strategy {
2092 FillNullStrategy::Forward(limit) => {
2093 let py_limit = limit
2094 .map(|v| PyInt::new(py, v).into())
2095 .unwrap_or_else(|| py.None());
2096 ("forward", py_limit)
2097 },
2098 FillNullStrategy::Backward(limit) => {
2099 let py_limit = limit
2100 .map(|v| PyInt::new(py, v).into())
2101 .unwrap_or_else(|| py.None());
2102 ("backward", py_limit)
2103 },
2104 FillNullStrategy::Min => ("min", py.None()),
2105 FillNullStrategy::Max => ("max", py.None()),
2106 FillNullStrategy::Mean => ("mean", py.None()),
2107 FillNullStrategy::Zero => ("zero", py.None()),
2108 FillNullStrategy::One => ("one", py.None()),
2109 };
2110
2111 ("fill_null_with_strategy", strategy_str, py_limit).into_py_any(py)
2112 },
2113 IRFunctionExpr::GatherEvery { n, offset } => {
2114 ("gather_every", offset, n).into_py_any(py)
2115 },
2116 IRFunctionExpr::Reinterpret(dtype) => {
2117 ("reinterpret", &Wrap(dtype.clone())).into_py_any(py)
2118 },
2119 IRFunctionExpr::ExtendConstant => ("extend_constant",).into_py_any(py),
2120 IRFunctionExpr::Business(_) => {
2121 return Err(PyNotImplementedError::new_err("business"));
2122 },
2123 #[cfg(feature = "top_k")]
2124 IRFunctionExpr::TopKBy { descending } => ("top_k_by", descending).into_py_any(py),
2125 IRFunctionExpr::EwmMeanBy { half_life } => {
2126 (PyEwmFunction::MeanBy, Wrap(*half_life)).into_py_any(py)
2127 },
2128 IRFunctionExpr::EwmSumBy { half_life } => {
2129 (PyEwmFunction::SumBy, Wrap(*half_life)).into_py_any(py)
2130 },
2131 IRFunctionExpr::RowEncode(..) => {
2132 return Err(PyNotImplementedError::new_err("row_encode"));
2133 },
2134 IRFunctionExpr::RowDecode(..) => {
2135 return Err(PyNotImplementedError::new_err("row_decode"));
2136 },
2137 IRFunctionExpr::DynamicPred { pred } => {
2138 ("dynamic_pred", pred.id().map(|u| u.as_u128())).into_py_any(py)
2139 },
2140 }?,
2141 options: py.None(),
2142 }
2143 .into_py_any(py),
2144 AExpr::Rolling {
2145 function,
2146 index_column,
2147 period,
2148 offset,
2149 closed_window,
2150 } => Rolling {
2151 function: function.0,
2152 index_column: index_column.0,
2153 period: Wrap(*period).into_py_any(py)?,
2154 offset: Wrap(*offset).into_py_any(py)?,
2155 closed_window: Wrap(*closed_window).into_py_any(py)?,
2156 }
2157 .into_py_any(py),
2158 AExpr::Over {
2159 function,
2160 partition_by,
2161 order_by,
2162 mapping,
2163 } => {
2164 let function = function.0;
2165 let partition_by = partition_by.iter().map(|n| n.0).collect();
2166 let order_by_descending = order_by
2167 .map(|(_, options)| options.descending)
2168 .unwrap_or(false);
2169 let order_by_nulls_last = order_by
2170 .map(|(_, options)| options.nulls_last)
2171 .unwrap_or(false);
2172 let order_by = order_by.map(|(n, _)| n.0);
2173
2174 let options = PyWindowMapping { inner: *mapping }.into_py_any(py)?;
2175 Window {
2176 function,
2177 partition_by,
2178 order_by,
2179 order_by_descending,
2180 order_by_nulls_last,
2181 options,
2182 }
2183 .into_py_any(py)
2184 },
2185 AExpr::Slice {
2186 input,
2187 offset,
2188 length,
2189 } => Slice {
2190 input: input.0,
2191 offset: offset.0,
2192 length: length.0,
2193 }
2194 .into_py_any(py),
2195 AExpr::Len => Len {}.into_py_any(py),
2196 AExpr::Eval { .. } => Err(PyNotImplementedError::new_err("list.eval")),
2197 AExpr::StructEval { expr, evaluation } => StructEval {
2198 expr: expr.0,
2199 evaluation: evaluation.iter().map(|e| e.into()).collect(),
2200 }
2201 .into_py_any(py),
2202 }
2203}