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