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