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