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