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