1#[cfg(feature = "iejoin")]
2use polars::prelude::InequalityOperator;
3use polars::series::ops::NullBehavior;
4use polars_compute::rolling::QuantileMethod;
5use polars_core::chunked_array::ops::FillNullStrategy;
6#[cfg(feature = "string_normalize")]
7use polars_ops::chunked_array::UnicodeForm;
8use polars_ops::prelude::RankMethod;
9use polars_ops::series::InterpolationMethod;
10#[cfg(feature = "search_sorted")]
11use polars_ops::series::SearchSortedSide;
12use polars_plan::plans::{
13 DynLiteralValue, IRBooleanFunction, IRFunctionExpr, IRPowFunction, IRRollingFunctionBy,
14 IRStringFunction, IRStructFunction, IRTemporalFunction,
15};
16use polars_plan::prelude::{
17 AExpr, GroupbyOptions, IRAggExpr, LiteralValue, Operator, WindowMapping,
18};
19use polars_time::prelude::RollingGroupOptions;
20use polars_time::{ClosedWindow, Duration, DynamicGroupOptions};
21use pyo3::IntoPyObjectExt;
22use pyo3::exceptions::PyNotImplementedError;
23use pyo3::prelude::*;
24use pyo3::types::{PyInt, PyTuple};
25
26use crate::Wrap;
27use crate::series::PySeries;
28
29#[pyclass(frozen)]
30pub struct Alias {
31 #[pyo3(get)]
32 expr: usize,
33 #[pyo3(get)]
34 name: Py<PyAny>,
35}
36
37#[pyclass(frozen)]
38pub struct Column {
39 #[pyo3(get)]
40 name: Py<PyAny>,
41}
42
43#[pyclass(frozen)]
44pub struct Literal {
45 #[pyo3(get)]
46 value: Py<PyAny>,
47 #[pyo3(get)]
48 dtype: Py<PyAny>,
49}
50
51#[pyclass(name = "Operator", eq, frozen, skip_from_py_object)]
52#[derive(Copy, Clone, PartialEq)]
53pub enum PyOperator {
54 Eq,
55 EqValidity,
56 NotEq,
57 NotEqValidity,
58 Lt,
59 LtEq,
60 Gt,
61 GtEq,
62 Plus,
63 Minus,
64 Multiply,
65 Divide,
66 TrueDivide,
67 FloorDivide,
68 Modulus,
69 And,
70 Or,
71 Xor,
72 LogicalAnd,
73 LogicalOr,
74}
75
76#[pymethods]
77impl PyOperator {
78 fn __hash__(&self) -> isize {
79 *self as isize
80 }
81}
82
83impl<'py> IntoPyObject<'py> for Wrap<Operator> {
84 type Target = PyOperator;
85 type Output = Bound<'py, Self::Target>;
86 type Error = PyErr;
87
88 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
89 match self.0 {
90 Operator::Eq => PyOperator::Eq,
91 Operator::EqValidity => PyOperator::EqValidity,
92 Operator::NotEq => PyOperator::NotEq,
93 Operator::NotEqValidity => PyOperator::NotEqValidity,
94 Operator::Lt => PyOperator::Lt,
95 Operator::LtEq => PyOperator::LtEq,
96 Operator::Gt => PyOperator::Gt,
97 Operator::GtEq => PyOperator::GtEq,
98 Operator::Plus => PyOperator::Plus,
99 Operator::Minus => PyOperator::Minus,
100 Operator::Multiply => PyOperator::Multiply,
101 Operator::RustDivide => PyOperator::Divide,
102 Operator::TrueDivide => PyOperator::TrueDivide,
103 Operator::FloorDivide => PyOperator::FloorDivide,
104 Operator::Modulus => PyOperator::Modulus,
105 Operator::And => PyOperator::And,
106 Operator::Or => PyOperator::Or,
107 Operator::Xor => PyOperator::Xor,
108 Operator::LogicalAnd => PyOperator::LogicalAnd,
109 Operator::LogicalOr => PyOperator::LogicalOr,
110 }
111 .into_pyobject(py)
112 }
113}
114
115#[cfg(feature = "iejoin")]
116impl<'py> IntoPyObject<'py> for Wrap<InequalityOperator> {
117 type Target = PyOperator;
118 type Output = Bound<'py, Self::Target>;
119 type Error = PyErr;
120
121 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
122 match self.0 {
123 InequalityOperator::Lt => PyOperator::Lt,
124 InequalityOperator::LtEq => PyOperator::LtEq,
125 InequalityOperator::Gt => PyOperator::Gt,
126 InequalityOperator::GtEq => PyOperator::GtEq,
127 }
128 .into_pyobject(py)
129 }
130}
131
132#[pyclass(name = "StringFunction", eq, frozen, skip_from_py_object)]
133#[derive(Copy, Clone, PartialEq)]
134pub enum PyStringFunction {
135 ConcatHorizontal,
136 ConcatVertical,
137 Contains,
138 CountMatches,
139 EndsWith,
140 Extract,
141 ExtractAll,
142 ExtractGroups,
143 Find,
144 ToInteger,
145 LenBytes,
146 LenChars,
147 Lowercase,
148 JsonDecode,
149 JsonPathMatch,
150 Replace,
151 Reverse,
152 PadStart,
153 PadEnd,
154 Slice,
155 Head,
156 Tail,
157 HexEncode,
158 HexDecode,
159 Base64Encode,
160 Base64Decode,
161 StartsWith,
162 StripChars,
163 StripCharsStart,
164 StripCharsEnd,
165 StripPrefix,
166 StripSuffix,
167 SplitExact,
168 SplitN,
169 Strptime,
170 Split,
171 SplitRegex,
172 ToDecimal,
173 Titlecase,
174 Uppercase,
175 ZFill,
176 ContainsAny,
177 ReplaceMany,
178 EscapeRegex,
179 Normalize,
180}
181
182#[pymethods]
183impl PyStringFunction {
184 fn __hash__(&self) -> isize {
185 *self as isize
186 }
187}
188
189#[pyclass(name = "BooleanFunction", eq, frozen, skip_from_py_object)]
190#[derive(Copy, Clone, PartialEq)]
191pub enum PyBooleanFunction {
192 Any,
193 All,
194 IsEmpty,
195 HasNulls,
196 IsNull,
197 IsNotNull,
198 IsFinite,
199 IsInfinite,
200 IsNan,
201 IsNotNan,
202 IsFirstDistinct,
203 IsLastDistinct,
204 IsUnique,
205 IsDuplicated,
206 IsBetween,
207 IsIn,
208 IsClose,
209 AllHorizontal,
210 AnyHorizontal,
211 Not,
212}
213
214#[pymethods]
215impl PyBooleanFunction {
216 fn __hash__(&self) -> isize {
217 *self as isize
218 }
219}
220
221#[pyclass(name = "TemporalFunction", eq, frozen, skip_from_py_object)]
222#[derive(Copy, Clone, PartialEq)]
223pub enum PyTemporalFunction {
224 Millennium,
225 Century,
226 Year,
227 IsLeapYear,
228 IsoYear,
229 Quarter,
230 Month,
231 DaysInMonth,
232 Week,
233 WeekDay,
234 Day,
235 OrdinalDay,
236 Time,
237 Date,
238 Datetime,
239 Duration,
240 Hour,
241 Minute,
242 Second,
243 Millisecond,
244 Microsecond,
245 Nanosecond,
246 TotalDays,
247 TotalHours,
248 TotalMinutes,
249 TotalSeconds,
250 TotalMilliseconds,
251 TotalMicroseconds,
252 TotalNanoseconds,
253 ToString,
254 CastTimeUnit,
255 WithTimeUnit,
256 ConvertTimeZone,
257 TimeStamp,
258 Truncate,
259 OffsetBy,
260 MonthStart,
261 MonthEnd,
262 BaseUtcOffset,
263 DSTOffset,
264 Round,
265 Replace,
266 ReplaceTimeZone,
267 Combine,
268 DatetimeFunction,
269}
270
271#[pymethods]
272impl PyTemporalFunction {
273 fn __hash__(&self) -> isize {
274 *self as isize
275 }
276}
277
278#[pyclass(name = "StructFunction", eq, frozen, skip_from_py_object)]
279#[derive(Copy, Clone, PartialEq)]
280pub enum PyStructFunction {
281 FieldByName,
282 RenameFields,
283 PrefixFields,
284 SuffixFields,
285 JsonEncode,
286 WithFields,
287 MapFieldNames,
288}
289
290#[pymethods]
291impl PyStructFunction {
292 fn __hash__(&self) -> isize {
293 *self as isize
294 }
295}
296
297#[pyclass(frozen)]
298pub struct BinaryExpr {
299 #[pyo3(get)]
300 left: usize,
301 #[pyo3(get)]
302 op: Py<PyAny>,
303 #[pyo3(get)]
304 right: usize,
305}
306
307#[pyclass(frozen)]
308pub struct Cast {
309 #[pyo3(get)]
310 expr: usize,
311 #[pyo3(get)]
312 dtype: Py<PyAny>,
313 #[pyo3(get)]
317 options: u8,
318}
319
320#[pyclass(frozen)]
321pub struct Sort {
322 #[pyo3(get)]
323 expr: usize,
324 #[pyo3(get)]
325 options: (bool, bool, bool),
327}
328
329#[pyclass(frozen)]
330pub struct Gather {
331 #[pyo3(get)]
332 expr: usize,
333 #[pyo3(get)]
334 idx: usize,
335 #[pyo3(get)]
336 scalar: bool,
337}
338
339#[pyclass(frozen)]
340pub struct Filter {
341 #[pyo3(get)]
342 input: usize,
343 #[pyo3(get)]
344 by: usize,
345}
346
347#[pyclass(frozen)]
348pub struct SortBy {
349 #[pyo3(get)]
350 expr: usize,
351 #[pyo3(get)]
352 by: Vec<usize>,
353 #[pyo3(get)]
354 sort_options: (bool, Vec<bool>, Vec<bool>),
356}
357
358#[pyclass(frozen)]
359pub struct Agg {
360 #[pyo3(get)]
361 name: Py<PyAny>,
362 #[pyo3(get)]
363 arguments: Vec<usize>,
364 #[pyo3(get)]
365 options: Py<PyAny>,
367}
368
369#[pyclass(frozen)]
370pub struct Ternary {
371 #[pyo3(get)]
372 predicate: usize,
373 #[pyo3(get)]
374 truthy: usize,
375 #[pyo3(get)]
376 falsy: usize,
377}
378
379#[pyclass(frozen)]
380pub struct Function {
381 #[pyo3(get)]
382 input: Vec<usize>,
383 #[pyo3(get)]
384 function_data: Py<PyAny>,
385 #[pyo3(get)]
386 options: Py<PyAny>,
387}
388
389#[pyclass(frozen)]
390pub struct Slice {
391 #[pyo3(get)]
392 input: usize,
393 #[pyo3(get)]
394 offset: usize,
395 #[pyo3(get)]
396 length: usize,
397}
398
399#[pyclass(frozen)]
400pub struct Len {}
401
402#[pyclass(frozen)]
403pub struct Window {
404 #[pyo3(get)]
405 function: usize,
406 #[pyo3(get)]
407 partition_by: Vec<usize>,
408 #[pyo3(get)]
409 order_by: Option<usize>,
410 #[pyo3(get)]
411 order_by_descending: bool,
412 #[pyo3(get)]
413 order_by_nulls_last: bool,
414 #[pyo3(get)]
415 options: Py<PyAny>,
416}
417
418#[pyclass(frozen)]
419pub struct Rolling {
420 #[pyo3(get)]
421 function: usize,
422 #[pyo3(get)]
423 index_column: usize,
424 #[pyo3(get)]
425 period: Py<PyAny>,
426 #[pyo3(get)]
427 offset: Py<PyAny>,
428 #[pyo3(get)]
429 closed_window: Py<PyAny>,
430}
431
432#[pyclass(name = "WindowMapping", frozen)]
433pub struct PyWindowMapping {
434 inner: WindowMapping,
435}
436
437#[pymethods]
438impl PyWindowMapping {
439 #[getter]
440 fn kind(&self) -> &str {
441 self.inner.into()
442 }
443}
444
445impl<'py> IntoPyObject<'py> for Wrap<Duration> {
446 type Target = PyTuple;
447 type Output = Bound<'py, Self::Target>;
448 type Error = PyErr;
449
450 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
451 (
452 self.0.months(),
453 self.0.weeks(),
454 self.0.days(),
455 self.0.nanoseconds(),
456 self.0.parsed_int,
457 self.0.negative(),
458 )
459 .into_pyobject(py)
460 }
461}
462
463impl<'py> IntoPyObject<'py> for Wrap<ClosedWindow> {
464 type Target = PyAny;
465 type Output = Bound<'py, Self::Target>;
466 type Error = PyErr;
467
468 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
469 let s = match self.0 {
470 ClosedWindow::Left => "left",
471 ClosedWindow::Right => "right",
472 ClosedWindow::Both => "both",
473 ClosedWindow::None => "none",
474 };
475 Ok(s.into_pyobject(py)?.into_any())
476 }
477}
478
479#[pyclass(name = "RollingGroupOptions", frozen)]
480pub struct PyRollingGroupOptions {
481 inner: RollingGroupOptions,
482}
483
484#[pymethods]
485impl PyRollingGroupOptions {
486 #[getter]
487 fn index_column(&self) -> &str {
488 self.inner.index_column.as_str()
489 }
490
491 #[getter]
492 fn period(&self) -> Wrap<Duration> {
493 Wrap(self.inner.period)
494 }
495
496 #[getter]
497 fn offset(&self) -> Wrap<Duration> {
498 Wrap(self.inner.offset)
499 }
500
501 #[getter]
502 fn closed_window(&self) -> &str {
503 self.inner.closed_window.into()
504 }
505}
506
507#[pyclass(name = "DynamicGroupOptions", frozen)]
508pub struct PyDynamicGroupOptions {
509 inner: DynamicGroupOptions,
510}
511
512#[pymethods]
513impl PyDynamicGroupOptions {
514 #[getter]
515 fn index_column(&self) -> &str {
516 self.inner.index_column.as_str()
517 }
518
519 #[getter]
520 fn every(&self) -> Wrap<Duration> {
521 Wrap(self.inner.every)
522 }
523
524 #[getter]
525 fn period(&self) -> Wrap<Duration> {
526 Wrap(self.inner.period)
527 }
528
529 #[getter]
530 fn offset(&self) -> Wrap<Duration> {
531 Wrap(self.inner.offset)
532 }
533
534 #[getter]
535 fn label(&self) -> &str {
536 self.inner.label.into()
537 }
538
539 #[getter]
540 fn include_boundaries(&self) -> bool {
541 self.inner.include_boundaries
542 }
543
544 #[getter]
545 fn closed_window(&self) -> &str {
546 self.inner.closed_window.into()
547 }
548 #[getter]
549 fn start_by(&self) -> &str {
550 self.inner.start_by.into()
551 }
552}
553
554#[pyclass(name = "GroupbyOptions", frozen)]
555pub struct PyGroupbyOptions {
556 inner: GroupbyOptions,
557}
558
559impl PyGroupbyOptions {
560 pub(crate) fn new(inner: GroupbyOptions) -> Self {
561 Self { inner }
562 }
563}
564
565#[pymethods]
566impl PyGroupbyOptions {
567 #[getter]
568 fn slice(&self) -> Option<(i64, usize)> {
569 self.inner.slice
570 }
571
572 #[getter]
573 fn dynamic(&self) -> Option<PyDynamicGroupOptions> {
574 self.inner
575 .dynamic
576 .as_ref()
577 .map(|f| PyDynamicGroupOptions { inner: f.clone() })
578 }
579
580 #[getter]
581 fn rolling(&self) -> Option<PyRollingGroupOptions> {
582 self.inner
583 .rolling
584 .as_ref()
585 .map(|f| PyRollingGroupOptions { inner: f.clone() })
586 }
587}
588
589pub(crate) fn into_py(py: Python<'_>, expr: &AExpr) -> PyResult<Py<PyAny>> {
590 match expr {
591 AExpr::Element => Err(PyNotImplementedError::new_err("element")),
592 AExpr::Explode { .. } => Err(PyNotImplementedError::new_err("explode")),
593 AExpr::Column(name) => Column {
594 name: name.into_py_any(py)?,
595 }
596 .into_py_any(py),
597 AExpr::StructField(_) => Err(PyNotImplementedError::new_err("field")),
598 AExpr::Literal(lit) => {
599 use polars_core::prelude::AnyValue;
600 let dtype: Py<PyAny> = Wrap(lit.get_datatype()).into_py_any(py)?;
601 let py_value = match lit {
602 LiteralValue::Dyn(d) => match d {
603 DynLiteralValue::Int(v) => v.into_py_any(py)?,
604 DynLiteralValue::Float(v) => v.into_py_any(py)?,
605 DynLiteralValue::Str(v) => v.into_py_any(py)?,
606 DynLiteralValue::List(_) => todo!(),
607 },
608 LiteralValue::Scalar(sc) => {
609 match sc.as_any_value() {
610 AnyValue::Duration(delta, _) => delta.into_py_any(py)?,
615 any => Wrap(any).into_py_any(py)?,
616 }
617 },
618 LiteralValue::Range(_) => {
619 return Err(PyNotImplementedError::new_err("range literal"));
620 },
621 LiteralValue::Series(s) => PySeries::new((**s).clone()).into_py_any(py)?,
622 };
623
624 Literal {
625 value: py_value,
626 dtype,
627 }
628 }
629 .into_py_any(py),
630 AExpr::BinaryExpr { left, op, right } => BinaryExpr {
631 left: left.0,
632 op: Wrap(*op).into_py_any(py)?,
633 right: right.0,
634 }
635 .into_py_any(py),
636 AExpr::Cast {
637 expr,
638 dtype,
639 options,
640 } => Cast {
641 expr: expr.0,
642 dtype: Wrap(dtype.clone()).into_py_any(py)?,
643 options: *options as u8,
644 }
645 .into_py_any(py),
646 AExpr::Sort { expr, options } => Sort {
647 expr: expr.0,
648 options: (
649 options.maintain_order,
650 options.nulls_last,
651 options.descending,
652 ),
653 }
654 .into_py_any(py),
655 AExpr::Gather {
656 expr,
657 idx,
658 returns_scalar,
659 null_on_oob: _,
660 } => Gather {
661 expr: expr.0,
662 idx: idx.0,
663 scalar: *returns_scalar,
664 }
665 .into_py_any(py),
666 AExpr::Filter { input, by } => Filter {
667 input: input.0,
668 by: by.0,
669 }
670 .into_py_any(py),
671 AExpr::SortBy {
672 expr,
673 by,
674 sort_options,
675 } => SortBy {
676 expr: expr.0,
677 by: by.iter().map(|n| n.0).collect(),
678 sort_options: (
679 sort_options.maintain_order,
680 sort_options.nulls_last.clone(),
681 sort_options.descending.clone(),
682 ),
683 }
684 .into_py_any(py),
685 AExpr::Agg(aggexpr) => match aggexpr {
686 IRAggExpr::Min {
687 input,
688 propagate_nans,
689 } => Agg {
690 name: "min".into_py_any(py)?,
691 arguments: vec![input.0],
692 options: propagate_nans.into_py_any(py)?,
693 },
694 IRAggExpr::Max {
695 input,
696 propagate_nans,
697 } => Agg {
698 name: "max".into_py_any(py)?,
699 arguments: vec![input.0],
700 options: propagate_nans.into_py_any(py)?,
701 },
702 IRAggExpr::Median(n) => Agg {
703 name: "median".into_py_any(py)?,
704 arguments: vec![n.0],
705 options: py.None(),
706 },
707 IRAggExpr::NUnique(n) => Agg {
708 name: "n_unique".into_py_any(py)?,
709 arguments: vec![n.0],
710 options: py.None(),
711 },
712 IRAggExpr::First(n) => Agg {
713 name: "first".into_py_any(py)?,
714 arguments: vec![n.0],
715 options: py.None(),
716 },
717 IRAggExpr::FirstNonNull(n) => Agg {
718 name: "first_non_null".into_py_any(py)?,
719 arguments: vec![n.0],
720 options: py.None(),
721 },
722 IRAggExpr::Last(n) => Agg {
723 name: "last".into_py_any(py)?,
724 arguments: vec![n.0],
725 options: py.None(),
726 },
727 IRAggExpr::LastNonNull(n) => Agg {
728 name: "last_non_null".into_py_any(py)?,
729 arguments: vec![n.0],
730 options: py.None(),
731 },
732 IRAggExpr::Item {
733 input: n,
734 allow_empty,
735 } => Agg {
736 name: "item".into_py_any(py)?,
737 arguments: vec![n.0],
738 options: allow_empty.into_py_any(py)?,
739 },
740 IRAggExpr::Mean(n) => Agg {
741 name: "mean".into_py_any(py)?,
742 arguments: vec![n.0],
743 options: py.None(),
744 },
745 IRAggExpr::Implode {
746 input: n,
747 maintain_order,
748 } => Agg {
749 name: "implode".into_py_any(py)?,
750 arguments: vec![n.0],
751 options: maintain_order.into_py_any(py)?,
752 },
753 IRAggExpr::Sum(n) => Agg {
754 name: "sum".into_py_any(py)?,
755 arguments: vec![n.0],
756 options: py.None(),
757 },
758 IRAggExpr::Count {
759 input: n,
760 include_nulls,
761 } => Agg {
762 name: "count".into_py_any(py)?,
763 arguments: vec![n.0],
764 options: include_nulls.into_py_any(py)?,
765 },
766 IRAggExpr::Std(n, ddof) => Agg {
767 name: "std".into_py_any(py)?,
768 arguments: vec![n.0],
769 options: ddof.into_py_any(py)?,
770 },
771 IRAggExpr::Var(n, ddof) => Agg {
772 name: "var".into_py_any(py)?,
773 arguments: vec![n.0],
774 options: ddof.into_py_any(py)?,
775 },
776 IRAggExpr::AggGroups(n) => Agg {
777 name: "agg_groups".into_py_any(py)?,
778 arguments: vec![n.0],
779 options: py.None(),
780 },
781 }
782 .into_py_any(py),
783 AExpr::Ternary {
784 predicate,
785 truthy,
786 falsy,
787 } => Ternary {
788 predicate: predicate.0,
789 truthy: truthy.0,
790 falsy: falsy.0,
791 }
792 .into_py_any(py),
793 AExpr::AnonymousFunction { .. } => Err(PyNotImplementedError::new_err("anonymousfunction")),
794 AExpr::AnonymousAgg { .. } => {
795 Err(PyNotImplementedError::new_err("anonymous_streaming_agg"))
796 },
797 AExpr::Function {
798 input,
799 function,
800 options: _,
802 } => Function {
803 input: input.iter().map(|n| n.node().0).collect(),
804 function_data: match function {
805 IRFunctionExpr::ArrayExpr(_) => {
806 return Err(PyNotImplementedError::new_err("array expr"));
807 },
808 IRFunctionExpr::BinaryExpr(_) => {
809 return Err(PyNotImplementedError::new_err("binary expr"));
810 },
811 IRFunctionExpr::Categorical(_) => {
812 return Err(PyNotImplementedError::new_err("categorical expr"));
813 },
814 IRFunctionExpr::Extension(_) => {
815 return Err(PyNotImplementedError::new_err("extension expr"));
816 },
817 IRFunctionExpr::ListExpr(_) => {
818 return Err(PyNotImplementedError::new_err("list expr"));
819 },
820 IRFunctionExpr::Bitwise(_) => {
821 return Err(PyNotImplementedError::new_err("bitwise expr"));
822 },
823 IRFunctionExpr::StringExpr(strfun) => match strfun {
824 IRStringFunction::Format { .. } => {
825 return Err(PyNotImplementedError::new_err("bitwise expr"));
826 },
827 IRStringFunction::ConcatHorizontal {
828 delimiter,
829 ignore_nulls,
830 } => (
831 PyStringFunction::ConcatHorizontal,
832 delimiter.as_str(),
833 ignore_nulls,
834 )
835 .into_py_any(py),
836 IRStringFunction::ConcatVertical {
837 delimiter,
838 ignore_nulls,
839 } => (
840 PyStringFunction::ConcatVertical,
841 delimiter.as_str(),
842 ignore_nulls,
843 )
844 .into_py_any(py),
845 #[cfg(feature = "regex")]
846 IRStringFunction::Contains { literal, strict } => {
847 (PyStringFunction::Contains, literal, strict).into_py_any(py)
848 },
849 IRStringFunction::CountMatches(literal) => {
850 (PyStringFunction::CountMatches, literal).into_py_any(py)
851 },
852 IRStringFunction::EndsWith => (PyStringFunction::EndsWith,).into_py_any(py),
853 IRStringFunction::Extract(group_index) => {
854 (PyStringFunction::Extract, group_index).into_py_any(py)
855 },
856 IRStringFunction::ExtractAll => (PyStringFunction::ExtractAll,).into_py_any(py),
857 #[cfg(feature = "extract_groups")]
858 IRStringFunction::ExtractGroups { dtype, pat } => (
859 PyStringFunction::ExtractGroups,
860 &Wrap(dtype.clone()),
861 pat.as_str(),
862 )
863 .into_py_any(py),
864 #[cfg(feature = "regex")]
865 IRStringFunction::Find { literal, strict } => {
866 (PyStringFunction::Find, literal, strict).into_py_any(py)
867 },
868 IRStringFunction::ToInteger { dtype: _, strict } => {
869 (PyStringFunction::ToInteger, strict).into_py_any(py)
870 },
871 IRStringFunction::LenBytes => (PyStringFunction::LenBytes,).into_py_any(py),
872 IRStringFunction::LenChars => (PyStringFunction::LenChars,).into_py_any(py),
873 IRStringFunction::Lowercase => (PyStringFunction::Lowercase,).into_py_any(py),
874 #[cfg(feature = "extract_jsonpath")]
875 IRStringFunction::JsonDecode(_) => {
876 (PyStringFunction::JsonDecode, <Option<usize>>::None).into_py_any(py)
877 },
878 #[cfg(feature = "extract_jsonpath")]
879 IRStringFunction::JsonPathMatch => {
880 (PyStringFunction::JsonPathMatch,).into_py_any(py)
881 },
882 #[cfg(feature = "regex")]
883 IRStringFunction::Replace { n, literal } => {
884 (PyStringFunction::Replace, n, literal).into_py_any(py)
885 },
886 #[cfg(feature = "string_normalize")]
887 IRStringFunction::Normalize { form } => (
888 PyStringFunction::Normalize,
889 match form {
890 UnicodeForm::NFC => "nfc",
891 UnicodeForm::NFKC => "nfkc",
892 UnicodeForm::NFD => "nfd",
893 UnicodeForm::NFKD => "nfkd",
894 },
895 )
896 .into_py_any(py),
897 IRStringFunction::Reverse => (PyStringFunction::Reverse,).into_py_any(py),
898 IRStringFunction::PadStart { fill_char } => {
899 (PyStringFunction::PadStart, fill_char).into_py_any(py)
900 },
901 IRStringFunction::PadEnd { fill_char } => {
902 (PyStringFunction::PadEnd, fill_char).into_py_any(py)
903 },
904 IRStringFunction::Slice => (PyStringFunction::Slice,).into_py_any(py),
905 IRStringFunction::Head => (PyStringFunction::Head,).into_py_any(py),
906 IRStringFunction::Tail => (PyStringFunction::Tail,).into_py_any(py),
907 IRStringFunction::HexEncode => (PyStringFunction::HexEncode,).into_py_any(py),
908 #[cfg(feature = "binary_encoding")]
909 IRStringFunction::HexDecode(strict) => {
910 (PyStringFunction::HexDecode, strict).into_py_any(py)
911 },
912 IRStringFunction::Base64Encode => {
913 (PyStringFunction::Base64Encode,).into_py_any(py)
914 },
915 #[cfg(feature = "binary_encoding")]
916 IRStringFunction::Base64Decode(strict) => {
917 (PyStringFunction::Base64Decode, strict).into_py_any(py)
918 },
919 IRStringFunction::StartsWith => (PyStringFunction::StartsWith,).into_py_any(py),
920 IRStringFunction::StripChars => (PyStringFunction::StripChars,).into_py_any(py),
921 IRStringFunction::StripCharsStart => {
922 (PyStringFunction::StripCharsStart,).into_py_any(py)
923 },
924 IRStringFunction::StripCharsEnd => {
925 (PyStringFunction::StripCharsEnd,).into_py_any(py)
926 },
927 IRStringFunction::StripPrefix => {
928 (PyStringFunction::StripPrefix,).into_py_any(py)
929 },
930 IRStringFunction::StripSuffix => {
931 (PyStringFunction::StripSuffix,).into_py_any(py)
932 },
933 IRStringFunction::SplitExact { n, inclusive } => {
934 (PyStringFunction::SplitExact, n, inclusive).into_py_any(py)
935 },
936 IRStringFunction::SplitN(n) => (PyStringFunction::SplitN, n).into_py_any(py),
937 IRStringFunction::Strptime(_, options) => (
938 PyStringFunction::Strptime,
939 options.format.as_ref().map(|s| s.as_str()),
940 options.strict,
941 options.exact,
942 options.cache,
943 )
944 .into_py_any(py),
945 IRStringFunction::Split(inclusive) => {
946 (PyStringFunction::Split, inclusive).into_py_any(py)
947 },
948 IRStringFunction::SplitRegex { inclusive, strict } => {
949 (PyStringFunction::SplitRegex, inclusive, strict).into_py_any(py)
950 },
951 IRStringFunction::ToDecimal { scale } => {
952 (PyStringFunction::ToDecimal, scale).into_py_any(py)
953 },
954 #[cfg(feature = "nightly")]
955 IRStringFunction::Titlecase => (PyStringFunction::Titlecase,).into_py_any(py),
956 IRStringFunction::Uppercase => (PyStringFunction::Uppercase,).into_py_any(py),
957 IRStringFunction::ZFill => (PyStringFunction::ZFill,).into_py_any(py),
958 #[cfg(feature = "find_many")]
959 IRStringFunction::ContainsAny {
960 ascii_case_insensitive,
961 } => (PyStringFunction::ContainsAny, ascii_case_insensitive).into_py_any(py),
962 #[cfg(feature = "find_many")]
963 IRStringFunction::ReplaceMany {
964 ascii_case_insensitive,
965 leftmost,
966 } => (
967 PyStringFunction::ReplaceMany,
968 ascii_case_insensitive,
969 leftmost,
970 )
971 .into_py_any(py),
972 #[cfg(feature = "find_many")]
973 IRStringFunction::ExtractMany { .. } => {
974 return Err(PyNotImplementedError::new_err("extract_many"));
975 },
976 #[cfg(feature = "find_many")]
977 IRStringFunction::FindMany { .. } => {
978 return Err(PyNotImplementedError::new_err("find_many"));
979 },
980 #[cfg(feature = "regex")]
981 IRStringFunction::EscapeRegex => {
982 (PyStringFunction::EscapeRegex,).into_py_any(py)
983 },
984 },
985 IRFunctionExpr::StructExpr(fun) => match fun {
986 IRStructFunction::FieldByName(name) => {
987 (PyStructFunction::FieldByName, name.as_str()).into_py_any(py)
988 },
989 IRStructFunction::RenameFields(names) => {
990 (PyStructFunction::RenameFields, names[0].as_str()).into_py_any(py)
991 },
992 IRStructFunction::PrefixFields(prefix) => {
993 (PyStructFunction::PrefixFields, prefix.as_str()).into_py_any(py)
994 },
995 IRStructFunction::SuffixFields(prefix) => {
996 (PyStructFunction::SuffixFields, prefix.as_str()).into_py_any(py)
997 },
998 #[cfg(feature = "json")]
999 IRStructFunction::JsonEncode => (PyStructFunction::JsonEncode,).into_py_any(py),
1000 IRStructFunction::MapFieldNames(_) => {
1001 return Err(PyNotImplementedError::new_err("map_field_names"));
1002 },
1003 },
1004 IRFunctionExpr::TemporalExpr(fun) => match fun {
1005 IRTemporalFunction::Millennium => {
1006 (PyTemporalFunction::Millennium,).into_py_any(py)
1007 },
1008 IRTemporalFunction::Century => (PyTemporalFunction::Century,).into_py_any(py),
1009 IRTemporalFunction::Year => (PyTemporalFunction::Year,).into_py_any(py),
1010 IRTemporalFunction::IsLeapYear => {
1011 (PyTemporalFunction::IsLeapYear,).into_py_any(py)
1012 },
1013 IRTemporalFunction::IsoYear => (PyTemporalFunction::IsoYear,).into_py_any(py),
1014 IRTemporalFunction::Quarter => (PyTemporalFunction::Quarter,).into_py_any(py),
1015 IRTemporalFunction::Month => (PyTemporalFunction::Month,).into_py_any(py),
1016 IRTemporalFunction::Week => (PyTemporalFunction::Week,).into_py_any(py),
1017 IRTemporalFunction::WeekDay => (PyTemporalFunction::WeekDay,).into_py_any(py),
1018 IRTemporalFunction::Day => (PyTemporalFunction::Day,).into_py_any(py),
1019 IRTemporalFunction::OrdinalDay => {
1020 (PyTemporalFunction::OrdinalDay,).into_py_any(py)
1021 },
1022 IRTemporalFunction::Time => (PyTemporalFunction::Time,).into_py_any(py),
1023 IRTemporalFunction::Date => (PyTemporalFunction::Date,).into_py_any(py),
1024 IRTemporalFunction::Datetime => (PyTemporalFunction::Datetime,).into_py_any(py),
1025 IRTemporalFunction::Duration(time_unit) => {
1026 (PyTemporalFunction::Duration, Wrap(*time_unit)).into_py_any(py)
1027 },
1028 IRTemporalFunction::Hour => (PyTemporalFunction::Hour,).into_py_any(py),
1029 IRTemporalFunction::Minute => (PyTemporalFunction::Minute,).into_py_any(py),
1030 IRTemporalFunction::Second => (PyTemporalFunction::Second,).into_py_any(py),
1031 IRTemporalFunction::Millisecond => {
1032 (PyTemporalFunction::Millisecond,).into_py_any(py)
1033 },
1034 IRTemporalFunction::Microsecond => {
1035 (PyTemporalFunction::Microsecond,).into_py_any(py)
1036 },
1037 IRTemporalFunction::Nanosecond => {
1038 (PyTemporalFunction::Nanosecond,).into_py_any(py)
1039 },
1040 IRTemporalFunction::DaysInMonth => {
1041 (PyTemporalFunction::DaysInMonth,).into_py_any(py)
1042 },
1043 IRTemporalFunction::TotalDays { fractional } => {
1044 (PyTemporalFunction::TotalDays, fractional).into_py_any(py)
1045 },
1046 IRTemporalFunction::TotalHours { fractional } => {
1047 (PyTemporalFunction::TotalHours, fractional).into_py_any(py)
1048 },
1049 IRTemporalFunction::TotalMinutes { fractional } => {
1050 (PyTemporalFunction::TotalMinutes, fractional).into_py_any(py)
1051 },
1052 IRTemporalFunction::TotalSeconds { fractional } => {
1053 (PyTemporalFunction::TotalSeconds, fractional).into_py_any(py)
1054 },
1055 IRTemporalFunction::TotalMilliseconds { fractional } => {
1056 (PyTemporalFunction::TotalMilliseconds, fractional).into_py_any(py)
1057 },
1058 IRTemporalFunction::TotalMicroseconds { fractional } => {
1059 (PyTemporalFunction::TotalMicroseconds, fractional).into_py_any(py)
1060 },
1061 IRTemporalFunction::TotalNanoseconds { fractional } => {
1062 (PyTemporalFunction::TotalNanoseconds, fractional).into_py_any(py)
1063 },
1064 IRTemporalFunction::ToString(format) => {
1065 (PyTemporalFunction::ToString, format).into_py_any(py)
1066 },
1067 IRTemporalFunction::CastTimeUnit(time_unit) => {
1068 (PyTemporalFunction::CastTimeUnit, Wrap(*time_unit)).into_py_any(py)
1069 },
1070 IRTemporalFunction::WithTimeUnit(time_unit) => {
1071 (PyTemporalFunction::WithTimeUnit, Wrap(*time_unit)).into_py_any(py)
1072 },
1073 #[cfg(feature = "timezones")]
1074 IRTemporalFunction::ConvertTimeZone(time_zone) => {
1075 (PyTemporalFunction::ConvertTimeZone, time_zone.as_str()).into_py_any(py)
1076 },
1077 IRTemporalFunction::TimeStamp(time_unit) => {
1078 (PyTemporalFunction::TimeStamp, Wrap(*time_unit)).into_py_any(py)
1079 },
1080 IRTemporalFunction::Truncate => (PyTemporalFunction::Truncate,).into_py_any(py),
1081 IRTemporalFunction::OffsetBy => (PyTemporalFunction::OffsetBy,).into_py_any(py),
1082 IRTemporalFunction::MonthStart => {
1083 (PyTemporalFunction::MonthStart,).into_py_any(py)
1084 },
1085 IRTemporalFunction::MonthEnd => (PyTemporalFunction::MonthEnd,).into_py_any(py),
1086 #[cfg(feature = "timezones")]
1087 IRTemporalFunction::BaseUtcOffset => {
1088 (PyTemporalFunction::BaseUtcOffset,).into_py_any(py)
1089 },
1090 #[cfg(feature = "timezones")]
1091 IRTemporalFunction::DSTOffset => {
1092 (PyTemporalFunction::DSTOffset,).into_py_any(py)
1093 },
1094 IRTemporalFunction::Round => (PyTemporalFunction::Round,).into_py_any(py),
1095 IRTemporalFunction::Replace => (PyTemporalFunction::Replace).into_py_any(py),
1096 #[cfg(feature = "timezones")]
1097 IRTemporalFunction::ReplaceTimeZone(time_zone, non_existent) => (
1098 PyTemporalFunction::ReplaceTimeZone,
1099 time_zone.as_ref().map(|s| s.as_str()),
1100 Into::<&str>::into(non_existent),
1101 )
1102 .into_py_any(py),
1103 IRTemporalFunction::Combine(time_unit) => {
1104 (PyTemporalFunction::Combine, Wrap(*time_unit)).into_py_any(py)
1105 },
1106 IRTemporalFunction::DatetimeFunction {
1107 time_unit,
1108 time_zone,
1109 } => (
1110 PyTemporalFunction::DatetimeFunction,
1111 Wrap(*time_unit),
1112 time_zone.as_ref().map(|s| s.as_str()),
1113 )
1114 .into_py_any(py),
1115 },
1116 IRFunctionExpr::Boolean(boolfun) => match boolfun {
1117 IRBooleanFunction::Any { ignore_nulls } => {
1118 (PyBooleanFunction::Any, *ignore_nulls).into_py_any(py)
1119 },
1120 IRBooleanFunction::All { ignore_nulls } => {
1121 (PyBooleanFunction::All, *ignore_nulls).into_py_any(py)
1122 },
1123 IRBooleanFunction::IsEmpty { ignore_nulls } => {
1124 (PyBooleanFunction::IsEmpty, *ignore_nulls).into_py_any(py)
1125 },
1126 IRBooleanFunction::HasNulls => (PyBooleanFunction::HasNulls,).into_py_any(py),
1127 IRBooleanFunction::IsNull => (PyBooleanFunction::IsNull,).into_py_any(py),
1128 IRBooleanFunction::IsNotNull => (PyBooleanFunction::IsNotNull,).into_py_any(py),
1129 IRBooleanFunction::IsFinite => (PyBooleanFunction::IsFinite,).into_py_any(py),
1130 IRBooleanFunction::IsInfinite => {
1131 (PyBooleanFunction::IsInfinite,).into_py_any(py)
1132 },
1133 IRBooleanFunction::IsNan => (PyBooleanFunction::IsNan,).into_py_any(py),
1134 IRBooleanFunction::IsNotNan => (PyBooleanFunction::IsNotNan,).into_py_any(py),
1135 IRBooleanFunction::IsFirstDistinct => {
1136 (PyBooleanFunction::IsFirstDistinct,).into_py_any(py)
1137 },
1138 IRBooleanFunction::IsLastDistinct => {
1139 (PyBooleanFunction::IsLastDistinct,).into_py_any(py)
1140 },
1141 IRBooleanFunction::IsUnique => (PyBooleanFunction::IsUnique,).into_py_any(py),
1142 IRBooleanFunction::IsDuplicated => {
1143 (PyBooleanFunction::IsDuplicated,).into_py_any(py)
1144 },
1145 IRBooleanFunction::IsBetween { closed } => {
1146 (PyBooleanFunction::IsBetween, Into::<&str>::into(closed)).into_py_any(py)
1147 },
1148 #[cfg(feature = "is_in")]
1149 IRBooleanFunction::IsIn { nulls_equal } => {
1150 (PyBooleanFunction::IsIn, nulls_equal).into_py_any(py)
1151 },
1152 IRBooleanFunction::IsClose {
1153 abs_tol,
1154 rel_tol,
1155 nans_equal,
1156 } => (PyBooleanFunction::IsClose, abs_tol.0, rel_tol.0, nans_equal)
1157 .into_py_any(py),
1158 IRBooleanFunction::AllHorizontal => {
1159 (PyBooleanFunction::AllHorizontal,).into_py_any(py)
1160 },
1161 IRBooleanFunction::AnyHorizontal => {
1162 (PyBooleanFunction::AnyHorizontal,).into_py_any(py)
1163 },
1164 IRBooleanFunction::Not => (PyBooleanFunction::Not,).into_py_any(py),
1165 },
1166 IRFunctionExpr::Abs => ("abs",).into_py_any(py),
1167 #[cfg(feature = "hist")]
1168 IRFunctionExpr::Hist {
1169 bin_count,
1170 include_category,
1171 include_breakpoint,
1172 } => ("hist", bin_count, include_category, include_breakpoint).into_py_any(py),
1173 IRFunctionExpr::NullCount => ("null_count",).into_py_any(py),
1174 IRFunctionExpr::Pow(f) => match f {
1175 IRPowFunction::Generic => ("pow",).into_py_any(py),
1176 IRPowFunction::Sqrt => ("sqrt",).into_py_any(py),
1177 IRPowFunction::Cbrt => ("cbrt",).into_py_any(py),
1178 },
1179 IRFunctionExpr::Hash(seed, seed_1, seed_2, seed_3) => {
1180 ("hash", seed, seed_1, seed_2, seed_3).into_py_any(py)
1181 },
1182 IRFunctionExpr::ArgWhere => ("argwhere",).into_py_any(py),
1183 #[cfg(feature = "index_of")]
1184 IRFunctionExpr::IndexOf => ("index_of",).into_py_any(py),
1185 #[cfg(feature = "search_sorted")]
1186 IRFunctionExpr::SearchSorted { side, descending } => (
1187 "search_sorted",
1188 match side {
1189 SearchSortedSide::Any => "any",
1190 SearchSortedSide::Left => "left",
1191 SearchSortedSide::Right => "right",
1192 },
1193 descending,
1194 )
1195 .into_py_any(py),
1196 IRFunctionExpr::Range(_) => return Err(PyNotImplementedError::new_err("range")),
1197 #[cfg(feature = "trigonometry")]
1198 IRFunctionExpr::Trigonometry(trigfun) => {
1199 use polars_plan::plans::IRTrigonometricFunction;
1200
1201 match trigfun {
1202 IRTrigonometricFunction::Cos => ("cos",),
1203 IRTrigonometricFunction::Cot => ("cot",),
1204 IRTrigonometricFunction::Sin => ("sin",),
1205 IRTrigonometricFunction::Tan => ("tan",),
1206 IRTrigonometricFunction::ArcCos => ("arccos",),
1207 IRTrigonometricFunction::ArcSin => ("arcsin",),
1208 IRTrigonometricFunction::ArcTan => ("arctan",),
1209 IRTrigonometricFunction::Cosh => ("cosh",),
1210 IRTrigonometricFunction::Sinh => ("sinh",),
1211 IRTrigonometricFunction::Tanh => ("tanh",),
1212 IRTrigonometricFunction::ArcCosh => ("arccosh",),
1213 IRTrigonometricFunction::ArcSinh => ("arcsinh",),
1214 IRTrigonometricFunction::ArcTanh => ("arctanh",),
1215 IRTrigonometricFunction::Degrees => ("degrees",),
1216 IRTrigonometricFunction::Radians => ("radians",),
1217 }
1218 .into_py_any(py)
1219 },
1220 #[cfg(feature = "trigonometry")]
1221 IRFunctionExpr::Atan2 => ("atan2",).into_py_any(py),
1222 #[cfg(feature = "sign")]
1223 IRFunctionExpr::Sign => ("sign",).into_py_any(py),
1224 IRFunctionExpr::FillNull => ("fill_null",).into_py_any(py),
1225 IRFunctionExpr::RollingExpr { function, .. } => {
1226 return Err(PyNotImplementedError::new_err(format!("{function}")));
1227 },
1228 IRFunctionExpr::RollingExprBy { function_by, .. } => match function_by {
1229 IRRollingFunctionBy::MinBy => {
1230 return Err(PyNotImplementedError::new_err("rolling min by"));
1231 },
1232 IRRollingFunctionBy::MaxBy => {
1233 return Err(PyNotImplementedError::new_err("rolling max by"));
1234 },
1235 IRRollingFunctionBy::MeanBy => {
1236 return Err(PyNotImplementedError::new_err("rolling mean by"));
1237 },
1238 IRRollingFunctionBy::SumBy => {
1239 return Err(PyNotImplementedError::new_err("rolling sum by"));
1240 },
1241 IRRollingFunctionBy::QuantileBy => {
1242 return Err(PyNotImplementedError::new_err("rolling quantile by"));
1243 },
1244 IRRollingFunctionBy::VarBy => {
1245 return Err(PyNotImplementedError::new_err("rolling var by"));
1246 },
1247 IRRollingFunctionBy::StdBy => {
1248 return Err(PyNotImplementedError::new_err("rolling std by"));
1249 },
1250 IRRollingFunctionBy::RankBy => {
1251 return Err(PyNotImplementedError::new_err("rolling rank by"));
1252 },
1253 },
1254 IRFunctionExpr::Rechunk => ("rechunk",).into_py_any(py),
1255 IRFunctionExpr::ShiftAndFill => ("shift_and_fill",).into_py_any(py),
1256 IRFunctionExpr::Shift => ("shift",).into_py_any(py),
1257 IRFunctionExpr::DropNans => ("drop_nans",).into_py_any(py),
1258 IRFunctionExpr::DropNulls => ("drop_nulls",).into_py_any(py),
1259 IRFunctionExpr::Quantile { method } => {
1260 let method = match method {
1261 QuantileMethod::Nearest => "nearest",
1262 QuantileMethod::Lower => "lower",
1263 QuantileMethod::Higher => "higher",
1264 QuantileMethod::Midpoint => "midpoint",
1265 QuantileMethod::Linear => "linear",
1266 QuantileMethod::Equiprobable => "equiprobable",
1267 };
1268 ("quantile", method).into_py_any(py)
1269 },
1270 IRFunctionExpr::Mode { maintain_order } => {
1271 ("mode", *maintain_order).into_py_any(py)
1272 },
1273 IRFunctionExpr::Skew(bias) => ("skew", bias).into_py_any(py),
1274 IRFunctionExpr::Kurtosis(fisher, bias) => {
1275 ("kurtosis", fisher, bias).into_py_any(py)
1276 },
1277 IRFunctionExpr::Reshape(_) => {
1278 return Err(PyNotImplementedError::new_err("reshape"));
1279 },
1280 #[cfg(feature = "repeat_by")]
1281 IRFunctionExpr::RepeatBy => ("repeat_by",).into_py_any(py),
1282 IRFunctionExpr::ArgUnique => ("arg_unique",).into_py_any(py),
1283 IRFunctionExpr::ArgMin => ("arg_min",).into_py_any(py),
1284 IRFunctionExpr::ArgMax => ("arg_max",).into_py_any(py),
1285 IRFunctionExpr::MinBy => ("min_by",).into_py_any(py),
1286 IRFunctionExpr::MaxBy => ("max_by",).into_py_any(py),
1287 IRFunctionExpr::ArgSort {
1288 descending,
1289 nulls_last,
1290 } => ("arg_max", descending, nulls_last).into_py_any(py),
1291 IRFunctionExpr::Product => ("product",).into_py_any(py),
1292 IRFunctionExpr::Repeat => ("repeat",).into_py_any(py),
1293 IRFunctionExpr::Rank { options, seed } => {
1294 let method = match options.method {
1295 RankMethod::Average => "average",
1296 RankMethod::Min => "min",
1297 RankMethod::Max => "max",
1298 RankMethod::Dense => "dense",
1299 RankMethod::Ordinal => "ordinal",
1300 RankMethod::Random => "random",
1301 };
1302 ("rank", method, options.descending, seed.map(|s| s as i64)).into_py_any(py)
1303 },
1304 IRFunctionExpr::Clip { has_min, has_max } => {
1305 ("clip", has_min, has_max).into_py_any(py)
1306 },
1307 IRFunctionExpr::AsStruct => ("as_struct",).into_py_any(py),
1308 #[cfg(feature = "top_k")]
1309 IRFunctionExpr::TopK { descending } => ("top_k", descending).into_py_any(py),
1310 IRFunctionExpr::CumCount { reverse } => ("cum_count", reverse).into_py_any(py),
1311 IRFunctionExpr::CumSum { reverse } => ("cum_sum", reverse).into_py_any(py),
1312 IRFunctionExpr::CumProd { reverse } => ("cum_prod", reverse).into_py_any(py),
1313 IRFunctionExpr::CumMin { reverse } => ("cum_min", reverse).into_py_any(py),
1314 IRFunctionExpr::CumMax { reverse } => ("cum_max", reverse).into_py_any(py),
1315 IRFunctionExpr::Reverse => ("reverse",).into_py_any(py),
1316 IRFunctionExpr::ValueCounts {
1317 sort,
1318 parallel,
1319 name,
1320 normalize,
1321 } => ("value_counts", sort, parallel, name.as_str(), normalize).into_py_any(py),
1322 IRFunctionExpr::UniqueCounts => ("unique_counts",).into_py_any(py),
1323 IRFunctionExpr::ApproxNUnique => ("approx_n_unique",).into_py_any(py),
1324 IRFunctionExpr::Coalesce => ("coalesce",).into_py_any(py),
1325 IRFunctionExpr::Diff(null_behaviour) => (
1326 "diff",
1327 match null_behaviour {
1328 NullBehavior::Drop => "drop",
1329 NullBehavior::Ignore => "ignore",
1330 },
1331 )
1332 .into_py_any(py),
1333 #[cfg(feature = "pct_change")]
1334 IRFunctionExpr::PctChange => ("pct_change",).into_py_any(py),
1335 IRFunctionExpr::Interpolate(method) => (
1336 "interpolate",
1337 match method {
1338 InterpolationMethod::Linear => "linear",
1339 InterpolationMethod::Nearest => "nearest",
1340 },
1341 )
1342 .into_py_any(py),
1343 IRFunctionExpr::InterpolateBy => ("interpolate_by",).into_py_any(py),
1344 IRFunctionExpr::Entropy { base, normalize } => {
1345 ("entropy", base, normalize).into_py_any(py)
1346 },
1347 IRFunctionExpr::Log => ("log",).into_py_any(py),
1348 IRFunctionExpr::Log1p => ("log1p",).into_py_any(py),
1349 IRFunctionExpr::Exp => ("exp",).into_py_any(py),
1350 IRFunctionExpr::Unique(maintain_order) => {
1351 ("unique", maintain_order).into_py_any(py)
1352 },
1353 IRFunctionExpr::Round { decimals, mode } => {
1354 ("round", decimals, Into::<&str>::into(mode)).into_py_any(py)
1355 },
1356 IRFunctionExpr::RoundSF { digits } => ("round_sig_figs", digits).into_py_any(py),
1357 IRFunctionExpr::Truncate { decimals } => ("truncate", decimals).into_py_any(py),
1358 IRFunctionExpr::Floor => ("floor",).into_py_any(py),
1359 IRFunctionExpr::Ceil => ("ceil",).into_py_any(py),
1360 IRFunctionExpr::Fused(_) => return Err(PyNotImplementedError::new_err("fused")),
1361 IRFunctionExpr::ConcatExpr { .. } => {
1362 return Err(PyNotImplementedError::new_err("concat expr"));
1363 },
1364 IRFunctionExpr::Correlation { .. } => {
1365 return Err(PyNotImplementedError::new_err("corr"));
1366 },
1367 #[cfg(feature = "peaks")]
1368 IRFunctionExpr::PeakMin => ("peak_max",).into_py_any(py),
1369 #[cfg(feature = "peaks")]
1370 IRFunctionExpr::PeakMax => ("peak_min",).into_py_any(py),
1371 #[cfg(feature = "cutqcut")]
1372 IRFunctionExpr::Cut { .. } => return Err(PyNotImplementedError::new_err("cut")),
1373 #[cfg(feature = "cutqcut")]
1374 IRFunctionExpr::QCut { .. } => return Err(PyNotImplementedError::new_err("qcut")),
1375 #[cfg(feature = "rle")]
1376 IRFunctionExpr::RLE => ("rle",).into_py_any(py),
1377 #[cfg(feature = "rle")]
1378 IRFunctionExpr::RLEID => ("rle_id",).into_py_any(py),
1379 IRFunctionExpr::ToPhysical => ("to_physical",).into_py_any(py),
1380 IRFunctionExpr::Random { .. } => {
1381 return Err(PyNotImplementedError::new_err("random"));
1382 },
1383 IRFunctionExpr::SetSortedFlag(sorted) => {
1384 ("set_sorted", sorted.descending, sorted.nulls_last).into_py_any(py)
1385 },
1386 #[cfg(feature = "ffi_plugin")]
1387 IRFunctionExpr::FfiPlugin { .. } => {
1388 return Err(PyNotImplementedError::new_err("ffi plugin"));
1389 },
1390 IRFunctionExpr::FoldHorizontal { .. } => {
1391 Err(PyNotImplementedError::new_err("fold"))
1392 },
1393 IRFunctionExpr::ReduceHorizontal { .. } => {
1394 Err(PyNotImplementedError::new_err("reduce"))
1395 },
1396 IRFunctionExpr::CumReduceHorizontal { .. } => {
1397 Err(PyNotImplementedError::new_err("cum_reduce"))
1398 },
1399 IRFunctionExpr::CumFoldHorizontal { .. } => {
1400 Err(PyNotImplementedError::new_err("cum_fold"))
1401 },
1402 IRFunctionExpr::SumHorizontal { ignore_nulls } => {
1403 ("sum_horizontal", ignore_nulls).into_py_any(py)
1404 },
1405 IRFunctionExpr::MaxHorizontal => ("max_horizontal",).into_py_any(py),
1406 IRFunctionExpr::MeanHorizontal { ignore_nulls } => {
1407 ("mean_horizontal", ignore_nulls).into_py_any(py)
1408 },
1409 IRFunctionExpr::MinHorizontal => ("min_horizontal",).into_py_any(py),
1410 IRFunctionExpr::EwmMean { options: _ } => {
1411 return Err(PyNotImplementedError::new_err("ewm mean"));
1412 },
1413 IRFunctionExpr::EwmStd { options: _ } => {
1414 return Err(PyNotImplementedError::new_err("ewm std"));
1415 },
1416 IRFunctionExpr::EwmVar { options: _ } => {
1417 return Err(PyNotImplementedError::new_err("ewm var"));
1418 },
1419 IRFunctionExpr::Replace => ("replace",).into_py_any(py),
1420 IRFunctionExpr::ReplaceStrict { return_dtype: _ } => {
1421 ("replace_strict",).into_py_any(py)
1423 },
1424 IRFunctionExpr::Negate => ("negate",).into_py_any(py),
1425 IRFunctionExpr::FillNullWithStrategy(strategy) => {
1426 let (strategy_str, py_limit): (&str, Py<PyAny>) = match strategy {
1427 FillNullStrategy::Forward(limit) => {
1428 let py_limit = limit
1429 .map(|v| PyInt::new(py, v).into())
1430 .unwrap_or_else(|| py.None());
1431 ("forward", py_limit)
1432 },
1433 FillNullStrategy::Backward(limit) => {
1434 let py_limit = limit
1435 .map(|v| PyInt::new(py, v).into())
1436 .unwrap_or_else(|| py.None());
1437 ("backward", py_limit)
1438 },
1439 FillNullStrategy::Min => ("min", py.None()),
1440 FillNullStrategy::Max => ("max", py.None()),
1441 FillNullStrategy::Mean => ("mean", py.None()),
1442 FillNullStrategy::Zero => ("zero", py.None()),
1443 FillNullStrategy::One => ("one", py.None()),
1444 };
1445
1446 ("fill_null_with_strategy", strategy_str, py_limit).into_py_any(py)
1447 },
1448 IRFunctionExpr::GatherEvery { n, offset } => {
1449 ("gather_every", offset, n).into_py_any(py)
1450 },
1451 IRFunctionExpr::Reinterpret(dtype) => {
1452 ("reinterpret", &Wrap(dtype.clone())).into_py_any(py)
1453 },
1454 IRFunctionExpr::ExtendConstant => ("extend_constant",).into_py_any(py),
1455 IRFunctionExpr::Business(_) => {
1456 return Err(PyNotImplementedError::new_err("business"));
1457 },
1458 #[cfg(feature = "top_k")]
1459 IRFunctionExpr::TopKBy { descending } => ("top_k_by", descending).into_py_any(py),
1460 IRFunctionExpr::EwmMeanBy { half_life: _ } => {
1461 return Err(PyNotImplementedError::new_err("ewm_mean_by"));
1462 },
1463 IRFunctionExpr::RowEncode(..) => {
1464 return Err(PyNotImplementedError::new_err("row_encode"));
1465 },
1466 IRFunctionExpr::RowDecode(..) => {
1467 return Err(PyNotImplementedError::new_err("row_decode"));
1468 },
1469 IRFunctionExpr::DynamicPred { .. } => {
1470 return Err(PyNotImplementedError::new_err("dynamic_pred"));
1471 },
1472 }?,
1473 options: py.None(),
1474 }
1475 .into_py_any(py),
1476 AExpr::Rolling {
1477 function,
1478 index_column,
1479 period,
1480 offset,
1481 closed_window,
1482 } => Rolling {
1483 function: function.0,
1484 index_column: index_column.0,
1485 period: Wrap(*period).into_py_any(py)?,
1486 offset: Wrap(*offset).into_py_any(py)?,
1487 closed_window: Wrap(*closed_window).into_py_any(py)?,
1488 }
1489 .into_py_any(py),
1490 AExpr::Over {
1491 function,
1492 partition_by,
1493 order_by,
1494 mapping,
1495 } => {
1496 let function = function.0;
1497 let partition_by = partition_by.iter().map(|n| n.0).collect();
1498 let order_by_descending = order_by
1499 .map(|(_, options)| options.descending)
1500 .unwrap_or(false);
1501 let order_by_nulls_last = order_by
1502 .map(|(_, options)| options.nulls_last)
1503 .unwrap_or(false);
1504 let order_by = order_by.map(|(n, _)| n.0);
1505
1506 let options = PyWindowMapping { inner: *mapping }.into_py_any(py)?;
1507 Window {
1508 function,
1509 partition_by,
1510 order_by,
1511 order_by_descending,
1512 order_by_nulls_last,
1513 options,
1514 }
1515 .into_py_any(py)
1516 },
1517 AExpr::Slice {
1518 input,
1519 offset,
1520 length,
1521 } => Slice {
1522 input: input.0,
1523 offset: offset.0,
1524 length: length.0,
1525 }
1526 .into_py_any(py),
1527 AExpr::Len => Len {}.into_py_any(py),
1528 AExpr::Eval { .. } => Err(PyNotImplementedError::new_err("list.eval")),
1529 AExpr::StructEval { .. } => Err(PyNotImplementedError::new_err("struct.with_fields")),
1530 }
1531}