Skip to main content

polars_python/expr/
general.rs

1use std::hash::{BuildHasher, Hash, Hasher};
2use std::ops::Neg;
3
4use polars::lazy::dsl;
5use polars::prelude::*;
6use polars::series::ops::NullBehavior;
7use polars_core::chunked_array::cast::CastOptions;
8use polars_plan::plans::predicates::aexpr_to_skip_batch_predicate;
9use polars_plan::plans::{
10    AExprSorted, ExprToIRContext, RowEncodingVariant, node_to_expr, to_expr_ir,
11};
12use polars_utils::arena::Arena;
13use pyo3::class::basic::CompareOp;
14use pyo3::prelude::*;
15
16use super::datatype::PyDataTypeExpr;
17use super::selector::PySelector;
18use crate::conversion::{Wrap, parse_fill_null_strategy};
19use crate::error::PyPolarsErr;
20use crate::utils::EnterPolarsExt;
21use crate::{PyDataType, PyExpr};
22
23#[pymethods]
24impl PyExpr {
25    fn __richcmp__(&self, other: Self, op: CompareOp) -> Self {
26        match op {
27            CompareOp::Eq => self.eq(other),
28            CompareOp::Ne => self.neq(other),
29            CompareOp::Gt => self.gt(other),
30            CompareOp::Lt => self.lt(other),
31            CompareOp::Ge => self.gt_eq(other),
32            CompareOp::Le => self.lt_eq(other),
33        }
34    }
35
36    fn __hash__(&self) -> isize {
37        let mut state = PlFixedStateQuality::with_seed(0).build_hasher();
38        Hash::hash(&self.inner, &mut state);
39        state.finish() as _
40    }
41
42    fn __add__(&self, rhs: Self) -> PyResult<Self> {
43        Ok(dsl::binary_expr(self.inner.clone(), Operator::Plus, rhs.inner).into())
44    }
45    fn __sub__(&self, rhs: Self) -> PyResult<Self> {
46        Ok(dsl::binary_expr(self.inner.clone(), Operator::Minus, rhs.inner).into())
47    }
48    fn __mul__(&self, rhs: Self) -> PyResult<Self> {
49        Ok(dsl::binary_expr(self.inner.clone(), Operator::Multiply, rhs.inner).into())
50    }
51    fn __truediv__(&self, rhs: Self) -> PyResult<Self> {
52        Ok(dsl::binary_expr(self.inner.clone(), Operator::TrueDivide, rhs.inner).into())
53    }
54    fn __mod__(&self, rhs: Self) -> PyResult<Self> {
55        Ok(dsl::binary_expr(self.inner.clone(), Operator::Modulus, rhs.inner).into())
56    }
57    fn __floordiv__(&self, rhs: Self) -> PyResult<Self> {
58        Ok(dsl::binary_expr(self.inner.clone(), Operator::FloorDivide, rhs.inner).into())
59    }
60    fn __neg__(&self) -> PyResult<Self> {
61        Ok(self.inner.clone().neg().into())
62    }
63
64    fn to_str(&self) -> String {
65        format!("{:?}", self.inner)
66    }
67    fn eq(&self, other: Self) -> Self {
68        self.inner.clone().eq(other.inner).into()
69    }
70
71    fn eq_missing(&self, other: Self) -> Self {
72        self.inner.clone().eq_missing(other.inner).into()
73    }
74    fn neq(&self, other: Self) -> Self {
75        self.inner.clone().neq(other.inner).into()
76    }
77    fn neq_missing(&self, other: Self) -> Self {
78        self.inner.clone().neq_missing(other.inner).into()
79    }
80    fn gt(&self, other: Self) -> Self {
81        self.inner.clone().gt(other.inner).into()
82    }
83    fn gt_eq(&self, other: Self) -> Self {
84        self.inner.clone().gt_eq(other.inner).into()
85    }
86    fn lt_eq(&self, other: Self) -> Self {
87        self.inner.clone().lt_eq(other.inner).into()
88    }
89    fn lt(&self, other: Self) -> Self {
90        self.inner.clone().lt(other.inner).into()
91    }
92
93    fn alias(&self, name: &str) -> Self {
94        self.inner.clone().alias(name).into()
95    }
96    fn not_(&self) -> Self {
97        self.inner.clone().not().into()
98    }
99    fn is_null(&self) -> Self {
100        self.inner.clone().is_null().into()
101    }
102    fn is_not_null(&self) -> Self {
103        self.inner.clone().is_not_null().into()
104    }
105
106    fn is_infinite(&self) -> Self {
107        self.inner.clone().is_infinite().into()
108    }
109
110    fn is_finite(&self) -> Self {
111        self.inner.clone().is_finite().into()
112    }
113
114    fn is_nan(&self) -> Self {
115        self.inner.clone().is_nan().into()
116    }
117
118    fn is_not_nan(&self) -> Self {
119        self.inner.clone().is_not_nan().into()
120    }
121
122    fn min(&self) -> Self {
123        self.inner.clone().min().into()
124    }
125
126    fn max(&self) -> Self {
127        self.inner.clone().max().into()
128    }
129
130    fn min_by(&self, by: Self) -> Self {
131        self.inner.clone().min_by(by.inner).into()
132    }
133
134    fn max_by(&self, by: Self) -> Self {
135        self.inner.clone().max_by(by.inner).into()
136    }
137
138    #[cfg(feature = "propagate_nans")]
139    fn nan_max(&self) -> Self {
140        self.inner.clone().nan_max().into()
141    }
142    #[cfg(feature = "propagate_nans")]
143    fn nan_min(&self) -> Self {
144        self.inner.clone().nan_min().into()
145    }
146    fn mean(&self) -> Self {
147        self.inner.clone().mean().into()
148    }
149    fn median(&self) -> Self {
150        self.inner.clone().median().into()
151    }
152    fn sum(&self) -> Self {
153        self.inner.clone().sum().into()
154    }
155    fn n_unique(&self) -> Self {
156        self.inner.clone().n_unique().into()
157    }
158    fn arg_unique(&self) -> Self {
159        self.inner.clone().arg_unique().into()
160    }
161    fn unique(&self) -> Self {
162        self.inner.clone().unique().into()
163    }
164    fn unique_stable(&self) -> Self {
165        self.inner.clone().unique_stable().into()
166    }
167    fn first(&self, ignore_nulls: bool) -> Self {
168        if ignore_nulls {
169            self.inner.clone().first_non_null().into()
170        } else {
171            self.inner.clone().first().into()
172        }
173    }
174    fn last(&self, ignore_nulls: bool) -> Self {
175        if ignore_nulls {
176            self.inner.clone().last_non_null().into()
177        } else {
178            self.inner.clone().last().into()
179        }
180    }
181    fn item(&self, allow_empty: bool) -> Self {
182        self.inner.clone().item(allow_empty).into()
183    }
184    fn implode(&self, maintain_order: bool) -> Self {
185        self.inner.clone().implode(maintain_order).into()
186    }
187    fn quantile(&self, quantile: Self, interpolation: Wrap<QuantileMethod>) -> Self {
188        self.inner
189            .clone()
190            .quantile(quantile.inner, interpolation.0)
191            .into()
192    }
193
194    #[pyo3(signature = (breaks, labels, left_closed, include_breaks))]
195    #[cfg(feature = "cutqcut")]
196    fn cut(
197        &self,
198        breaks: Vec<f64>,
199        labels: Option<Vec<String>>,
200        left_closed: bool,
201        include_breaks: bool,
202    ) -> Self {
203        self.inner
204            .clone()
205            .cut(breaks, labels, left_closed, include_breaks)
206            .into()
207    }
208    #[pyo3(signature = (probs, labels, left_closed, allow_duplicates, include_breaks))]
209    #[cfg(feature = "cutqcut")]
210    fn qcut(
211        &self,
212        probs: Vec<f64>,
213        labels: Option<Vec<String>>,
214        left_closed: bool,
215        allow_duplicates: bool,
216        include_breaks: bool,
217    ) -> Self {
218        self.inner
219            .clone()
220            .qcut(probs, labels, left_closed, allow_duplicates, include_breaks)
221            .into()
222    }
223    #[pyo3(signature = (n_bins, labels, left_closed, allow_duplicates, include_breaks))]
224    #[cfg(feature = "cutqcut")]
225    fn qcut_uniform(
226        &self,
227        n_bins: usize,
228        labels: Option<Vec<String>>,
229        left_closed: bool,
230        allow_duplicates: bool,
231        include_breaks: bool,
232    ) -> Self {
233        self.inner
234            .clone()
235            .qcut_uniform(
236                n_bins,
237                labels,
238                left_closed,
239                allow_duplicates,
240                include_breaks,
241            )
242            .into()
243    }
244
245    #[cfg(feature = "rle")]
246    fn rle(&self) -> Self {
247        self.inner.clone().rle().into()
248    }
249    #[cfg(feature = "rle")]
250    fn rle_id(&self) -> Self {
251        self.inner.clone().rle_id().into()
252    }
253
254    fn agg_groups(&self) -> Self {
255        self.inner.clone().agg_groups().into()
256    }
257    fn count(&self) -> Self {
258        self.inner.clone().count().into()
259    }
260    fn len(&self) -> Self {
261        self.inner.clone().len().into()
262    }
263    fn value_counts(&self, sort: bool, parallel: bool, name: String, normalize: bool) -> Self {
264        self.inner
265            .clone()
266            .value_counts(sort, parallel, name.as_str(), normalize)
267            .into()
268    }
269    fn unique_counts(&self) -> Self {
270        self.inner.clone().unique_counts().into()
271    }
272    fn null_count(&self) -> Self {
273        self.inner.clone().null_count().into()
274    }
275    fn cast(&self, dtype: PyDataTypeExpr, strict: bool, wrap_numerical: bool) -> Self {
276        let options = if wrap_numerical {
277            CastOptions::Overflowing
278        } else if strict {
279            CastOptions::Strict
280        } else {
281            CastOptions::NonStrict
282        };
283
284        let expr = self.inner.clone().cast_with_options(dtype.inner, options);
285        expr.into()
286    }
287    fn sort_with(&self, descending: bool, nulls_last: bool) -> Self {
288        self.inner
289            .clone()
290            .sort(SortOptions {
291                descending,
292                nulls_last,
293                multithreaded: true,
294                maintain_order: false,
295                limit: None,
296            })
297            .into()
298    }
299
300    fn arg_sort(&self, descending: bool, nulls_last: bool) -> Self {
301        self.inner.clone().arg_sort(descending, nulls_last).into()
302    }
303
304    #[cfg(feature = "top_k")]
305    fn top_k(&self, k: Self) -> Self {
306        self.inner.clone().top_k(k.inner).into()
307    }
308
309    #[cfg(feature = "top_k")]
310    fn top_k_by(&self, by: Vec<Self>, k: Self, reverse: Vec<bool>) -> Self {
311        let by = by.into_iter().map(|e| e.inner).collect::<Vec<_>>();
312        self.inner.clone().top_k_by(k.inner, by, reverse).into()
313    }
314
315    #[cfg(feature = "top_k")]
316    fn bottom_k(&self, k: Self) -> Self {
317        self.inner.clone().bottom_k(k.inner).into()
318    }
319
320    #[cfg(feature = "top_k")]
321    fn bottom_k_by(&self, by: Vec<Self>, k: Self, reverse: Vec<bool>) -> Self {
322        let by = by.into_iter().map(|e| e.inner).collect::<Vec<_>>();
323        self.inner.clone().bottom_k_by(k.inner, by, reverse).into()
324    }
325
326    #[cfg(feature = "peaks")]
327    fn peak_min(&self) -> Self {
328        self.inner.clone().peak_min().into()
329    }
330
331    #[cfg(feature = "peaks")]
332    fn peak_max(&self) -> Self {
333        self.inner.clone().peak_max().into()
334    }
335
336    fn arg_max(&self) -> Self {
337        self.inner.clone().arg_max().into()
338    }
339
340    fn arg_min(&self) -> Self {
341        self.inner.clone().arg_min().into()
342    }
343
344    #[cfg(feature = "index_of")]
345    fn index_of(&self, element: Self) -> Self {
346        self.inner.clone().index_of(element.inner).into()
347    }
348
349    #[cfg(feature = "search_sorted")]
350    #[pyo3(signature = (element, side, descending))]
351    fn search_sorted(&self, element: Self, side: Wrap<SearchSortedSide>, descending: bool) -> Self {
352        self.inner
353            .clone()
354            .search_sorted(element.inner, side.0, descending)
355            .into()
356    }
357
358    #[pyo3(signature = (idx, null_on_oob=false))]
359    fn gather(&self, idx: Self, null_on_oob: bool) -> Self {
360        self.inner.clone().gather(idx.inner, null_on_oob).into()
361    }
362
363    #[pyo3(signature = (idx, null_on_oob=false))]
364    fn get(&self, idx: Self, null_on_oob: bool) -> Self {
365        self.inner.clone().get(idx.inner, null_on_oob).into()
366    }
367
368    fn sort_by(
369        &self,
370        by: Vec<Self>,
371        descending: Vec<bool>,
372        nulls_last: Vec<bool>,
373        multithreaded: bool,
374        maintain_order: bool,
375    ) -> Self {
376        let by = by.into_iter().map(|e| e.inner).collect::<Vec<_>>();
377        self.inner
378            .clone()
379            .sort_by(
380                by,
381                SortMultipleOptions {
382                    descending,
383                    nulls_last,
384                    multithreaded,
385                    maintain_order,
386                    limit: None,
387                },
388            )
389            .into()
390    }
391
392    #[pyo3(signature = (n, fill_value))]
393    fn shift(&self, n: Self, fill_value: Option<Self>) -> Self {
394        let expr = self.inner.clone();
395        let out = match fill_value {
396            Some(v) => expr.shift_and_fill(n.inner, v.inner),
397            None => expr.shift(n.inner),
398        };
399        out.into()
400    }
401
402    fn fill_null(&self, expr: Self) -> Self {
403        self.inner.clone().fill_null(expr.inner).into()
404    }
405
406    fn fill_null_with_strategy(&self, strategy: &str, limit: FillNullLimit) -> PyResult<Self> {
407        let strategy = parse_fill_null_strategy(strategy, limit)?;
408        Ok(self.inner.clone().fill_null_with_strategy(strategy).into())
409    }
410
411    fn fill_nan(&self, expr: Self) -> Self {
412        self.inner.clone().fill_nan(expr.inner).into()
413    }
414
415    fn drop_nulls(&self) -> Self {
416        self.inner.clone().drop_nulls().into()
417    }
418
419    fn drop_nans(&self) -> Self {
420        self.inner.clone().drop_nans().into()
421    }
422
423    fn filter(&self, predicate: Self) -> Self {
424        self.inner.clone().filter(predicate.inner).into()
425    }
426
427    fn reverse(&self) -> Self {
428        self.inner.clone().reverse().into()
429    }
430
431    fn std(&self, ddof: u8) -> Self {
432        self.inner.clone().std(ddof).into()
433    }
434
435    fn var(&self, ddof: u8) -> Self {
436        self.inner.clone().var(ddof).into()
437    }
438
439    fn is_unique(&self) -> Self {
440        self.inner.clone().is_unique().into()
441    }
442
443    fn is_between(&self, lower: Self, upper: Self, closed: Wrap<ClosedInterval>) -> Self {
444        self.inner
445            .clone()
446            .is_between(lower.inner, upper.inner, closed.0)
447            .into()
448    }
449
450    fn is_close(&self, other: Self, abs_tol: f64, rel_tol: f64, nans_equal: bool) -> Self {
451        self.inner
452            .clone()
453            .is_close(other.inner, abs_tol, rel_tol, nans_equal)
454            .into()
455    }
456
457    fn is_sorted(&self, descending: Option<bool>, nulls_last: Option<bool>) -> Self {
458        self.inner.clone().is_sorted(descending, nulls_last).into()
459    }
460
461    #[cfg(feature = "approx_unique")]
462    fn approx_n_unique(&self) -> Self {
463        self.inner.clone().approx_n_unique().into()
464    }
465
466    fn is_first_distinct(&self) -> Self {
467        self.inner.clone().is_first_distinct().into()
468    }
469
470    fn is_last_distinct(&self) -> Self {
471        self.inner.clone().is_last_distinct().into()
472    }
473
474    fn explode(&self, empty_as_null: bool, keep_nulls: bool) -> Self {
475        self.inner
476            .clone()
477            .explode(ExplodeOptions {
478                empty_as_null,
479                keep_nulls,
480            })
481            .into()
482    }
483
484    fn gather_every(&self, n: usize, offset: usize) -> Self {
485        self.inner.clone().gather_every(n, offset).into()
486    }
487
488    fn slice(&self, offset: Self, length: Self) -> Self {
489        self.inner.clone().slice(offset.inner, length.inner).into()
490    }
491
492    fn append(&self, other: Self, upcast: bool) -> Self {
493        self.inner.clone().append(other.inner, upcast).into()
494    }
495
496    fn rechunk(&self) -> Self {
497        self.inner.clone().rechunk().into()
498    }
499
500    fn round(&self, decimals: u32, mode: Wrap<RoundMode>) -> Self {
501        self.inner.clone().round(decimals, mode.0).into()
502    }
503
504    fn round_sig_figs(&self, digits: i32) -> Self {
505        self.clone().inner.round_sig_figs(digits).into()
506    }
507
508    fn truncate(&self, decimals: u32) -> Self {
509        self.inner.clone().truncate(decimals).into()
510    }
511
512    fn floor(&self) -> Self {
513        self.inner.clone().floor().into()
514    }
515
516    fn ceil(&self) -> Self {
517        self.inner.clone().ceil().into()
518    }
519
520    #[pyo3(signature = (min, max))]
521    fn clip(&self, min: Option<Self>, max: Option<Self>) -> Self {
522        let expr = self.inner.clone();
523        let out = match (min, max) {
524            (Some(min), Some(max)) => expr.clip(min.inner, max.inner),
525            (Some(min), None) => expr.clip_min(min.inner),
526            (None, Some(max)) => expr.clip_max(max.inner),
527            (None, None) => expr,
528        };
529        out.into()
530    }
531
532    fn abs(&self) -> Self {
533        self.inner.clone().abs().into()
534    }
535
536    #[cfg(feature = "trigonometry")]
537    fn sin(&self) -> Self {
538        self.inner.clone().sin().into()
539    }
540
541    #[cfg(feature = "trigonometry")]
542    fn cos(&self) -> Self {
543        self.inner.clone().cos().into()
544    }
545
546    #[cfg(feature = "trigonometry")]
547    fn tan(&self) -> Self {
548        self.inner.clone().tan().into()
549    }
550
551    #[cfg(feature = "trigonometry")]
552    fn cot(&self) -> Self {
553        self.inner.clone().cot().into()
554    }
555
556    #[cfg(feature = "trigonometry")]
557    fn arcsin(&self) -> Self {
558        self.inner.clone().arcsin().into()
559    }
560
561    #[cfg(feature = "trigonometry")]
562    fn arccos(&self) -> Self {
563        self.inner.clone().arccos().into()
564    }
565
566    #[cfg(feature = "trigonometry")]
567    fn arctan(&self) -> Self {
568        self.inner.clone().arctan().into()
569    }
570
571    #[cfg(feature = "trigonometry")]
572    fn arctan2(&self, y: Self) -> Self {
573        self.inner.clone().arctan2(y.inner).into()
574    }
575
576    #[cfg(feature = "trigonometry")]
577    fn sinh(&self) -> Self {
578        self.inner.clone().sinh().into()
579    }
580
581    #[cfg(feature = "trigonometry")]
582    fn cosh(&self) -> Self {
583        self.inner.clone().cosh().into()
584    }
585
586    #[cfg(feature = "trigonometry")]
587    fn tanh(&self) -> Self {
588        self.inner.clone().tanh().into()
589    }
590
591    #[cfg(feature = "trigonometry")]
592    fn arcsinh(&self) -> Self {
593        self.inner.clone().arcsinh().into()
594    }
595
596    #[cfg(feature = "trigonometry")]
597    fn arccosh(&self) -> Self {
598        self.inner.clone().arccosh().into()
599    }
600
601    #[cfg(feature = "trigonometry")]
602    fn arctanh(&self) -> Self {
603        self.inner.clone().arctanh().into()
604    }
605
606    #[cfg(feature = "trigonometry")]
607    pub fn degrees(&self) -> Self {
608        self.inner.clone().degrees().into()
609    }
610
611    #[cfg(feature = "trigonometry")]
612    pub fn radians(&self) -> Self {
613        self.inner.clone().radians().into()
614    }
615
616    #[cfg(feature = "sign")]
617    fn sign(&self) -> Self {
618        self.inner.clone().sign().into()
619    }
620
621    fn is_duplicated(&self) -> Self {
622        self.inner.clone().is_duplicated().into()
623    }
624
625    #[pyo3(signature = (partition_by, order_by, order_by_descending, order_by_nulls_last, mapping_strategy))]
626    fn over(
627        &self,
628        partition_by: Option<Vec<Self>>,
629        order_by: Option<Vec<Self>>,
630        order_by_descending: bool,
631        order_by_nulls_last: bool,
632        mapping_strategy: Wrap<WindowMapping>,
633    ) -> PyResult<Self> {
634        let partition_by = partition_by.map(|partition_by| {
635            partition_by
636                .into_iter()
637                .map(|e| e.inner)
638                .collect::<Vec<Expr>>()
639        });
640
641        let order_by = order_by.map(|order_by| {
642            (
643                order_by.into_iter().map(|e| e.inner).collect::<Vec<Expr>>(),
644                SortOptions {
645                    descending: order_by_descending,
646                    nulls_last: order_by_nulls_last,
647                    maintain_order: false,
648                    ..Default::default()
649                },
650            )
651        });
652
653        Ok(self
654            .inner
655            .clone()
656            .over_with_options(partition_by, order_by, mapping_strategy.0)
657            .map_err(PyPolarsErr::from)?
658            .into())
659    }
660
661    fn rolling(
662        &self,
663        index_column: PyExpr,
664        period: &str,
665        offset: &str,
666        closed: Wrap<ClosedWindow>,
667    ) -> PyResult<Self> {
668        let period = Duration::try_parse(period).map_err(PyPolarsErr::from)?;
669        let offset = Duration::try_parse(offset).map_err(PyPolarsErr::from)?;
670        let closed = closed.0;
671
672        Ok(self
673            .inner
674            .clone()
675            .rolling(index_column.inner, period, offset, closed)
676            .into())
677    }
678
679    fn and_(&self, expr: Self) -> Self {
680        self.inner.clone().and(expr.inner).into()
681    }
682
683    fn or_(&self, expr: Self) -> Self {
684        self.inner.clone().or(expr.inner).into()
685    }
686
687    fn xor_(&self, expr: Self) -> Self {
688        self.inner.clone().xor(expr.inner).into()
689    }
690
691    #[cfg(feature = "is_in")]
692    fn is_in(&self, expr: Self, nulls_equal: bool) -> Self {
693        self.inner.clone().is_in(expr.inner, nulls_equal).into()
694    }
695
696    #[cfg(feature = "repeat_by")]
697    fn repeat_by(&self, by: Self) -> Self {
698        self.inner.clone().repeat_by(by.inner).into()
699    }
700
701    fn pow(&self, exponent: Self) -> Self {
702        self.inner.clone().pow(exponent.inner).into()
703    }
704
705    fn sqrt(&self) -> Self {
706        self.inner.clone().sqrt().into()
707    }
708
709    fn cbrt(&self) -> Self {
710        self.inner.clone().cbrt().into()
711    }
712
713    fn cum_sum(&self, reverse: bool) -> Self {
714        self.inner.clone().cum_sum(reverse).into()
715    }
716    fn cum_max(&self, reverse: bool) -> Self {
717        self.inner.clone().cum_max(reverse).into()
718    }
719    fn cum_min(&self, reverse: bool) -> Self {
720        self.inner.clone().cum_min(reverse).into()
721    }
722    fn cum_prod(&self, reverse: bool) -> Self {
723        self.inner.clone().cum_prod(reverse).into()
724    }
725    fn cum_count(&self, reverse: bool) -> Self {
726        self.inner.clone().cum_count(reverse).into()
727    }
728
729    fn cumulative_eval(&self, expr: Self, min_samples: usize) -> Self {
730        self.inner
731            .clone()
732            .cumulative_eval(expr.inner, min_samples)
733            .into()
734    }
735
736    fn product(&self) -> Self {
737        self.inner.clone().product().into()
738    }
739
740    fn dot(&self, other: Self) -> Self {
741        self.inner.clone().dot(other.inner).into()
742    }
743
744    fn reinterpret(&self, signed: Option<bool>, dtype: Option<PyDataType>) -> Self {
745        self.inner
746            .clone()
747            .reinterpret(signed, dtype.map(|dt| dt.0))
748            .into()
749    }
750    fn mode(&self, maintain_order: bool) -> Self {
751        self.inner.clone().mode(maintain_order).into()
752    }
753    fn interpolate(&self, method: Wrap<InterpolationMethod>) -> Self {
754        self.inner.clone().interpolate(method.0).into()
755    }
756    fn interpolate_by(&self, by: PyExpr) -> Self {
757        self.inner.clone().interpolate_by(by.inner).into()
758    }
759
760    fn lower_bound(&self) -> Self {
761        self.inner.clone().lower_bound().into()
762    }
763
764    fn upper_bound(&self) -> Self {
765        self.inner.clone().upper_bound().into()
766    }
767
768    #[pyo3(signature = (method, descending, seed))]
769    fn rank(&self, method: Wrap<RankMethod>, descending: bool, seed: Option<u64>) -> Self {
770        let options = RankOptions {
771            method: method.0,
772            descending,
773        };
774        self.inner.clone().rank(options, seed).into()
775    }
776
777    fn diff(&self, n: PyExpr, null_behavior: Wrap<NullBehavior>) -> Self {
778        self.inner.clone().diff(n.inner, null_behavior.0).into()
779    }
780
781    #[cfg(feature = "pct_change")]
782    fn pct_change(&self, n: Self) -> Self {
783        self.inner.clone().pct_change(n.inner).into()
784    }
785
786    fn skew(&self, bias: bool) -> Self {
787        self.inner.clone().skew(bias).into()
788    }
789    fn kurtosis(&self, fisher: bool, bias: bool) -> Self {
790        self.inner.clone().kurtosis(fisher, bias).into()
791    }
792
793    #[cfg(feature = "dtype-array")]
794    fn reshape(&self, dims: Vec<i64>) -> Self {
795        self.inner.clone().reshape(&dims).into()
796    }
797
798    fn to_physical(&self) -> Self {
799        self.inner.clone().to_physical().into()
800    }
801
802    #[pyo3(signature = (seed))]
803    fn shuffle(&self, seed: Option<u64>) -> Self {
804        self.inner.clone().shuffle(seed).into()
805    }
806
807    #[pyo3(signature = (n, with_replacement, shuffle, seed))]
808    fn sample_n(
809        &self,
810        n: Self,
811        with_replacement: bool,
812        shuffle: Option<bool>,
813        seed: Option<u64>,
814    ) -> Self {
815        self.inner
816            .clone()
817            .sample_n(n.inner, with_replacement, shuffle, seed)
818            .into()
819    }
820
821    #[pyo3(signature = (frac, with_replacement, shuffle, seed))]
822    fn sample_frac(
823        &self,
824        frac: Self,
825        with_replacement: bool,
826        shuffle: Option<bool>,
827        seed: Option<u64>,
828    ) -> Self {
829        self.inner
830            .clone()
831            .sample_frac(frac.inner, with_replacement, shuffle, seed)
832            .into()
833    }
834
835    fn ewm_mean(&self, alpha: f64, adjust: bool, min_periods: usize, ignore_nulls: bool) -> Self {
836        let options = EWMOptions {
837            alpha,
838            adjust,
839            bias: false,
840            min_periods,
841            ignore_nulls,
842        };
843        self.inner.clone().ewm_mean(options).into()
844    }
845    fn ewm_sum(&self, alpha: f64, min_periods: usize, ignore_nulls: bool) -> Self {
846        let options = EWMOptions {
847            alpha,
848            bias: false,
849            min_periods,
850            ignore_nulls,
851            ..Default::default()
852        };
853        self.inner.clone().ewm_sum(options).into()
854    }
855    fn ewm_mean_by(&self, times: PyExpr, half_life: &str) -> PyResult<Self> {
856        let half_life = Duration::try_parse(half_life).map_err(PyPolarsErr::from)?;
857        Ok(self
858            .inner
859            .clone()
860            .ewm_mean_by(times.inner, half_life)
861            .into())
862    }
863    fn ewm_sum_by(&self, times: PyExpr, half_life: &str) -> PyResult<Self> {
864        let half_life = Duration::try_parse(half_life).map_err(PyPolarsErr::from)?;
865        Ok(self.inner.clone().ewm_sum_by(times.inner, half_life).into())
866    }
867
868    fn ewm_std(
869        &self,
870        alpha: f64,
871        adjust: bool,
872        bias: bool,
873        min_periods: usize,
874        ignore_nulls: bool,
875    ) -> Self {
876        let options = EWMOptions {
877            alpha,
878            adjust,
879            bias,
880            min_periods,
881            ignore_nulls,
882        };
883        self.inner.clone().ewm_std(options).into()
884    }
885    fn ewm_var(
886        &self,
887        alpha: f64,
888        adjust: bool,
889        bias: bool,
890        min_periods: usize,
891        ignore_nulls: bool,
892    ) -> Self {
893        let options = EWMOptions {
894            alpha,
895            adjust,
896            bias,
897            min_periods,
898            ignore_nulls,
899        };
900        self.inner.clone().ewm_var(options).into()
901    }
902    fn extend_constant(&self, value: PyExpr, n: PyExpr) -> Self {
903        self.inner
904            .clone()
905            .extend_constant(value.inner, n.inner)
906            .into()
907    }
908
909    fn any(&self, ignore_nulls: bool) -> Self {
910        self.inner.clone().any(ignore_nulls).into()
911    }
912    fn all(&self, ignore_nulls: bool) -> Self {
913        self.inner.clone().all(ignore_nulls).into()
914    }
915    fn is_empty(&self, ignore_nulls: bool) -> Self {
916        self.inner.clone().is_empty(ignore_nulls).into()
917    }
918
919    fn has_nulls(&self) -> Self {
920        self.inner.clone().has_nulls().into()
921    }
922
923    fn log(&self, base: PyExpr) -> Self {
924        self.inner.clone().log(base.inner).into()
925    }
926
927    fn log1p(&self) -> Self {
928        self.inner.clone().log1p().into()
929    }
930
931    fn exp(&self) -> Self {
932        self.inner.clone().exp().into()
933    }
934
935    fn entropy(&self, base: f64, normalize: bool) -> Self {
936        self.inner.clone().entropy(base, normalize).into()
937    }
938    fn hash(&self, seed: u64, seed_1: u64, seed_2: u64, seed_3: u64) -> Self {
939        self.inner.clone().hash(seed, seed_1, seed_2, seed_3).into()
940    }
941    fn set_sorted_flag(&self, descending: bool, nulls_last: bool) -> Self {
942        let sortedness = AExprSorted::default()
943            .with_desc(Some(descending))
944            .with_nulls_last(Some(nulls_last));
945        self.inner.clone().set_sorted_flag(sortedness).into()
946    }
947
948    fn replace(&self, old: PyExpr, new: PyExpr) -> Self {
949        self.inner.clone().replace(old.inner, new.inner).into()
950    }
951
952    #[pyo3(signature = (old, new, default, return_dtype))]
953    fn replace_strict(
954        &self,
955        old: PyExpr,
956        new: PyExpr,
957        default: Option<PyExpr>,
958        return_dtype: Option<PyDataTypeExpr>,
959    ) -> Self {
960        self.inner
961            .clone()
962            .replace_strict(
963                old.inner,
964                new.inner,
965                default.map(|e| e.inner),
966                return_dtype.map(|dt| dt.inner),
967            )
968            .into()
969    }
970
971    #[cfg(feature = "hist")]
972    #[pyo3(signature = (bins, bin_count, include_category, include_breakpoint))]
973    fn hist(
974        &self,
975        bins: Option<PyExpr>,
976        bin_count: Option<usize>,
977        include_category: bool,
978        include_breakpoint: bool,
979    ) -> Self {
980        let bins = bins.map(|e| e.inner);
981        self.inner
982            .clone()
983            .hist(bins, bin_count, include_category, include_breakpoint)
984            .into()
985    }
986
987    #[pyo3(signature = (schema))]
988    fn skip_batch_predicate(&self, py: Python<'_>, schema: Wrap<Schema>) -> PyResult<Option<Self>> {
989        let mut aexpr_arena = Arena::new();
990        py.enter_polars(|| {
991            let mut ctx = ExprToIRContext::new(&mut aexpr_arena, &schema.0);
992            ctx.allow_unknown = true;
993            let node = to_expr_ir(self.inner.clone(), &mut ctx)?.node();
994            let Some(node) = aexpr_to_skip_batch_predicate(node, &mut aexpr_arena, &schema.0)
995            else {
996                return Ok(None);
997            };
998            let skip_batch_predicate = node_to_expr(node, &aexpr_arena);
999            PolarsResult::Ok(Some(Self {
1000                inner: skip_batch_predicate,
1001            }))
1002        })
1003    }
1004
1005    #[staticmethod]
1006    fn row_encode_unordered(exprs: Vec<Self>) -> Self {
1007        Expr::n_ary(
1008            FunctionExpr::RowEncode(RowEncodingVariant::Unordered),
1009            exprs.into_iter().map(|e| e.inner.clone()).collect(),
1010        )
1011        .into()
1012    }
1013
1014    #[staticmethod]
1015    fn row_encode_ordered(
1016        exprs: Vec<Self>,
1017        descending: Option<Vec<bool>>,
1018        nulls_last: Option<Vec<bool>>,
1019    ) -> Self {
1020        Expr::n_ary(
1021            FunctionExpr::RowEncode(RowEncodingVariant::Ordered {
1022                descending,
1023                nulls_last,
1024                broadcast_nulls: None,
1025            }),
1026            exprs.into_iter().map(|e| e.inner.clone()).collect(),
1027        )
1028        .into()
1029    }
1030
1031    fn row_decode_unordered(&self, names: Vec<String>, datatypes: Vec<PyDataTypeExpr>) -> Self {
1032        let fields = names
1033            .into_iter()
1034            .zip(datatypes)
1035            .map(|(name, dtype)| (PlSmallStr::from_string(name), dtype.inner))
1036            .collect();
1037        self.inner
1038            .clone()
1039            .map_unary(FunctionExpr::RowDecode(
1040                fields,
1041                RowEncodingVariant::Unordered,
1042            ))
1043            .into()
1044    }
1045
1046    fn row_decode_ordered(
1047        &self,
1048        names: Vec<String>,
1049        datatypes: Vec<PyDataTypeExpr>,
1050        descending: Option<Vec<bool>>,
1051        nulls_last: Option<Vec<bool>>,
1052    ) -> Self {
1053        let fields = names
1054            .into_iter()
1055            .zip(datatypes)
1056            .map(|(name, dtype)| (PlSmallStr::from_string(name), dtype.inner))
1057            .collect::<Vec<_>>();
1058        self.inner
1059            .clone()
1060            .map_unary(FunctionExpr::RowDecode(
1061                fields,
1062                RowEncodingVariant::Ordered {
1063                    descending,
1064                    nulls_last,
1065                    broadcast_nulls: None,
1066                },
1067            ))
1068            .into()
1069    }
1070
1071    #[allow(clippy::wrong_self_convention)]
1072    fn into_selector(&self) -> PyResult<PySelector> {
1073        Ok(self
1074            .inner
1075            .clone()
1076            .into_selector()
1077            .ok_or_else(
1078                || polars_err!(InvalidOperation: "expr `{}` is not a selector", &self.inner),
1079            )
1080            .map_err(PyPolarsErr::from)?
1081            .into())
1082    }
1083
1084    #[staticmethod]
1085    fn new_selector(selector: PySelector) -> Self {
1086        Expr::Selector(selector.inner).into()
1087    }
1088}