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