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