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