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, RowEncodingVariant, 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::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.clone().arg_sort(descending, nulls_last).into()
273 }
274
275 #[cfg(feature = "top_k")]
276 fn top_k(&self, k: Self) -> Self {
277 self.inner.clone().top_k(k.inner).into()
278 }
279
280 #[cfg(feature = "top_k")]
281 fn top_k_by(&self, by: Vec<Self>, k: Self, reverse: Vec<bool>) -> Self {
282 let by = by.into_iter().map(|e| e.inner).collect::<Vec<_>>();
283 self.inner.clone().top_k_by(k.inner, by, reverse).into()
284 }
285
286 #[cfg(feature = "top_k")]
287 fn bottom_k(&self, k: Self) -> Self {
288 self.inner.clone().bottom_k(k.inner).into()
289 }
290
291 #[cfg(feature = "top_k")]
292 fn bottom_k_by(&self, by: Vec<Self>, k: Self, reverse: Vec<bool>) -> Self {
293 let by = by.into_iter().map(|e| e.inner).collect::<Vec<_>>();
294 self.inner.clone().bottom_k_by(k.inner, by, reverse).into()
295 }
296
297 #[cfg(feature = "peaks")]
298 fn peak_min(&self) -> Self {
299 self.inner.clone().peak_min().into()
300 }
301
302 #[cfg(feature = "peaks")]
303 fn peak_max(&self) -> Self {
304 self.inner.clone().peak_max().into()
305 }
306
307 fn arg_max(&self) -> Self {
308 self.inner.clone().arg_max().into()
309 }
310
311 fn arg_min(&self) -> Self {
312 self.inner.clone().arg_min().into()
313 }
314
315 #[cfg(feature = "index_of")]
316 fn index_of(&self, element: Self) -> Self {
317 self.inner.clone().index_of(element.inner).into()
318 }
319
320 #[cfg(feature = "search_sorted")]
321 #[pyo3(signature = (element, side, descending))]
322 fn search_sorted(&self, element: Self, side: Wrap<SearchSortedSide>, descending: bool) -> Self {
323 self.inner
324 .clone()
325 .search_sorted(element.inner, side.0, descending)
326 .into()
327 }
328
329 fn gather(&self, idx: Self) -> Self {
330 self.inner.clone().gather(idx.inner).into()
331 }
332
333 fn get(&self, idx: Self) -> Self {
334 self.inner.clone().get(idx.inner).into()
335 }
336
337 fn sort_by(
338 &self,
339 by: Vec<Self>,
340 descending: Vec<bool>,
341 nulls_last: Vec<bool>,
342 multithreaded: bool,
343 maintain_order: bool,
344 ) -> Self {
345 let by = by.into_iter().map(|e| e.inner).collect::<Vec<_>>();
346 self.inner
347 .clone()
348 .sort_by(
349 by,
350 SortMultipleOptions {
351 descending,
352 nulls_last,
353 multithreaded,
354 maintain_order,
355 limit: None,
356 },
357 )
358 .into()
359 }
360
361 #[pyo3(signature = (n, fill_value))]
362 fn shift(&self, n: Self, fill_value: Option<Self>) -> Self {
363 let expr = self.inner.clone();
364 let out = match fill_value {
365 Some(v) => expr.shift_and_fill(n.inner, v.inner),
366 None => expr.shift(n.inner),
367 };
368 out.into()
369 }
370
371 fn fill_null(&self, expr: Self) -> Self {
372 self.inner.clone().fill_null(expr.inner).into()
373 }
374
375 fn fill_null_with_strategy(&self, strategy: &str, limit: FillNullLimit) -> PyResult<Self> {
376 let strategy = parse_fill_null_strategy(strategy, limit)?;
377 Ok(self.inner.clone().fill_null_with_strategy(strategy).into())
378 }
379
380 fn fill_nan(&self, expr: Self) -> Self {
381 self.inner.clone().fill_nan(expr.inner).into()
382 }
383
384 fn drop_nulls(&self) -> Self {
385 self.inner.clone().drop_nulls().into()
386 }
387
388 fn drop_nans(&self) -> Self {
389 self.inner.clone().drop_nans().into()
390 }
391
392 fn filter(&self, predicate: Self) -> Self {
393 self.inner.clone().filter(predicate.inner).into()
394 }
395
396 fn reverse(&self) -> Self {
397 self.inner.clone().reverse().into()
398 }
399
400 fn std(&self, ddof: u8) -> Self {
401 self.inner.clone().std(ddof).into()
402 }
403
404 fn var(&self, ddof: u8) -> Self {
405 self.inner.clone().var(ddof).into()
406 }
407
408 fn is_unique(&self) -> Self {
409 self.inner.clone().is_unique().into()
410 }
411
412 fn is_between(&self, lower: Self, upper: Self, closed: Wrap<ClosedInterval>) -> Self {
413 self.inner
414 .clone()
415 .is_between(lower.inner, upper.inner, closed.0)
416 .into()
417 }
418
419 fn is_close(&self, other: Self, abs_tol: f64, rel_tol: f64, nans_equal: bool) -> Self {
420 self.inner
421 .clone()
422 .is_close(other.inner, abs_tol, rel_tol, nans_equal)
423 .into()
424 }
425
426 #[cfg(feature = "approx_unique")]
427 fn approx_n_unique(&self) -> Self {
428 self.inner.clone().approx_n_unique().into()
429 }
430
431 fn is_first_distinct(&self) -> Self {
432 self.inner.clone().is_first_distinct().into()
433 }
434
435 fn is_last_distinct(&self) -> Self {
436 self.inner.clone().is_last_distinct().into()
437 }
438
439 fn explode(&self) -> Self {
440 self.inner.clone().explode().into()
441 }
442
443 fn gather_every(&self, n: usize, offset: usize) -> Self {
444 self.inner.clone().gather_every(n, offset).into()
445 }
446
447 fn slice(&self, offset: Self, length: Self) -> Self {
448 self.inner.clone().slice(offset.inner, length.inner).into()
449 }
450
451 fn append(&self, other: Self, upcast: bool) -> Self {
452 self.inner.clone().append(other.inner, upcast).into()
453 }
454
455 fn rechunk(&self) -> Self {
456 self.inner.clone().rechunk().into()
457 }
458
459 fn round(&self, decimals: u32, mode: Wrap<RoundMode>) -> Self {
460 self.inner.clone().round(decimals, mode.0).into()
461 }
462
463 fn round_sig_figs(&self, digits: i32) -> Self {
464 self.clone().inner.round_sig_figs(digits).into()
465 }
466
467 fn floor(&self) -> Self {
468 self.inner.clone().floor().into()
469 }
470
471 fn ceil(&self) -> Self {
472 self.inner.clone().ceil().into()
473 }
474
475 #[pyo3(signature = (min, max))]
476 fn clip(&self, min: Option<Self>, max: Option<Self>) -> Self {
477 let expr = self.inner.clone();
478 let out = match (min, max) {
479 (Some(min), Some(max)) => expr.clip(min.inner, max.inner),
480 (Some(min), None) => expr.clip_min(min.inner),
481 (None, Some(max)) => expr.clip_max(max.inner),
482 (None, None) => expr,
483 };
484 out.into()
485 }
486
487 fn abs(&self) -> Self {
488 self.inner.clone().abs().into()
489 }
490
491 #[cfg(feature = "trigonometry")]
492 fn sin(&self) -> Self {
493 self.inner.clone().sin().into()
494 }
495
496 #[cfg(feature = "trigonometry")]
497 fn cos(&self) -> Self {
498 self.inner.clone().cos().into()
499 }
500
501 #[cfg(feature = "trigonometry")]
502 fn tan(&self) -> Self {
503 self.inner.clone().tan().into()
504 }
505
506 #[cfg(feature = "trigonometry")]
507 fn cot(&self) -> Self {
508 self.inner.clone().cot().into()
509 }
510
511 #[cfg(feature = "trigonometry")]
512 fn arcsin(&self) -> Self {
513 self.inner.clone().arcsin().into()
514 }
515
516 #[cfg(feature = "trigonometry")]
517 fn arccos(&self) -> Self {
518 self.inner.clone().arccos().into()
519 }
520
521 #[cfg(feature = "trigonometry")]
522 fn arctan(&self) -> Self {
523 self.inner.clone().arctan().into()
524 }
525
526 #[cfg(feature = "trigonometry")]
527 fn arctan2(&self, y: Self) -> Self {
528 self.inner.clone().arctan2(y.inner).into()
529 }
530
531 #[cfg(feature = "trigonometry")]
532 fn sinh(&self) -> Self {
533 self.inner.clone().sinh().into()
534 }
535
536 #[cfg(feature = "trigonometry")]
537 fn cosh(&self) -> Self {
538 self.inner.clone().cosh().into()
539 }
540
541 #[cfg(feature = "trigonometry")]
542 fn tanh(&self) -> Self {
543 self.inner.clone().tanh().into()
544 }
545
546 #[cfg(feature = "trigonometry")]
547 fn arcsinh(&self) -> Self {
548 self.inner.clone().arcsinh().into()
549 }
550
551 #[cfg(feature = "trigonometry")]
552 fn arccosh(&self) -> Self {
553 self.inner.clone().arccosh().into()
554 }
555
556 #[cfg(feature = "trigonometry")]
557 fn arctanh(&self) -> Self {
558 self.inner.clone().arctanh().into()
559 }
560
561 #[cfg(feature = "trigonometry")]
562 pub fn degrees(&self) -> Self {
563 self.inner.clone().degrees().into()
564 }
565
566 #[cfg(feature = "trigonometry")]
567 pub fn radians(&self) -> Self {
568 self.inner.clone().radians().into()
569 }
570
571 #[cfg(feature = "sign")]
572 fn sign(&self) -> Self {
573 self.inner.clone().sign().into()
574 }
575
576 fn is_duplicated(&self) -> Self {
577 self.inner.clone().is_duplicated().into()
578 }
579
580 #[pyo3(signature = (partition_by, order_by, order_by_descending, order_by_nulls_last, mapping_strategy))]
581 fn over(
582 &self,
583 partition_by: Option<Vec<Self>>,
584 order_by: Option<Vec<Self>>,
585 order_by_descending: bool,
586 order_by_nulls_last: bool,
587 mapping_strategy: Wrap<WindowMapping>,
588 ) -> PyResult<Self> {
589 let partition_by = partition_by.map(|partition_by| {
590 partition_by
591 .into_iter()
592 .map(|e| e.inner)
593 .collect::<Vec<Expr>>()
594 });
595
596 let order_by = order_by.map(|order_by| {
597 (
598 order_by.into_iter().map(|e| e.inner).collect::<Vec<Expr>>(),
599 SortOptions {
600 descending: order_by_descending,
601 nulls_last: order_by_nulls_last,
602 maintain_order: false,
603 ..Default::default()
604 },
605 )
606 });
607
608 Ok(self
609 .inner
610 .clone()
611 .over_with_options(partition_by, order_by, mapping_strategy.0)
612 .map_err(PyPolarsErr::from)?
613 .into())
614 }
615
616 fn rolling(
617 &self,
618 index_column: &str,
619 period: &str,
620 offset: &str,
621 closed: Wrap<ClosedWindow>,
622 ) -> PyResult<Self> {
623 let options = RollingGroupOptions {
624 index_column: index_column.into(),
625 period: Duration::try_parse(period).map_err(PyPolarsErr::from)?,
626 offset: Duration::try_parse(offset).map_err(PyPolarsErr::from)?,
627 closed_window: closed.0,
628 };
629
630 Ok(self.inner.clone().rolling(options).into())
631 }
632
633 fn and_(&self, expr: Self) -> Self {
634 self.inner.clone().and(expr.inner).into()
635 }
636
637 fn or_(&self, expr: Self) -> Self {
638 self.inner.clone().or(expr.inner).into()
639 }
640
641 fn xor_(&self, expr: Self) -> Self {
642 self.inner.clone().xor(expr.inner).into()
643 }
644
645 #[cfg(feature = "is_in")]
646 fn is_in(&self, expr: Self, nulls_equal: bool) -> Self {
647 self.inner.clone().is_in(expr.inner, nulls_equal).into()
648 }
649
650 #[cfg(feature = "repeat_by")]
651 fn repeat_by(&self, by: Self) -> Self {
652 self.inner.clone().repeat_by(by.inner).into()
653 }
654
655 fn pow(&self, exponent: Self) -> Self {
656 self.inner.clone().pow(exponent.inner).into()
657 }
658
659 fn sqrt(&self) -> Self {
660 self.inner.clone().sqrt().into()
661 }
662
663 fn cbrt(&self) -> Self {
664 self.inner.clone().cbrt().into()
665 }
666
667 fn cum_sum(&self, reverse: bool) -> Self {
668 self.inner.clone().cum_sum(reverse).into()
669 }
670 fn cum_max(&self, reverse: bool) -> Self {
671 self.inner.clone().cum_max(reverse).into()
672 }
673 fn cum_min(&self, reverse: bool) -> Self {
674 self.inner.clone().cum_min(reverse).into()
675 }
676 fn cum_prod(&self, reverse: bool) -> Self {
677 self.inner.clone().cum_prod(reverse).into()
678 }
679 fn cum_count(&self, reverse: bool) -> Self {
680 self.inner.clone().cum_count(reverse).into()
681 }
682
683 fn cumulative_eval(&self, expr: Self, min_samples: usize) -> Self {
684 self.inner
685 .clone()
686 .cumulative_eval(expr.inner, min_samples)
687 .into()
688 }
689
690 fn product(&self) -> Self {
691 self.inner.clone().product().into()
692 }
693
694 fn dot(&self, other: Self) -> Self {
695 self.inner.clone().dot(other.inner).into()
696 }
697
698 fn reinterpret(&self, signed: bool) -> Self {
699 self.inner.clone().reinterpret(signed).into()
700 }
701 fn mode(&self) -> Self {
702 self.inner.clone().mode().into()
703 }
704 fn interpolate(&self, method: Wrap<InterpolationMethod>) -> Self {
705 self.inner.clone().interpolate(method.0).into()
706 }
707 fn interpolate_by(&self, by: PyExpr) -> Self {
708 self.inner.clone().interpolate_by(by.inner).into()
709 }
710
711 fn lower_bound(&self) -> Self {
712 self.inner.clone().lower_bound().into()
713 }
714
715 fn upper_bound(&self) -> Self {
716 self.inner.clone().upper_bound().into()
717 }
718
719 #[pyo3(signature = (method, descending, seed))]
720 fn rank(&self, method: Wrap<RankMethod>, descending: bool, seed: Option<u64>) -> Self {
721 let options = RankOptions {
722 method: method.0,
723 descending,
724 };
725 self.inner.clone().rank(options, seed).into()
726 }
727
728 fn diff(&self, n: PyExpr, null_behavior: Wrap<NullBehavior>) -> Self {
729 self.inner.clone().diff(n.inner, null_behavior.0).into()
730 }
731
732 #[cfg(feature = "pct_change")]
733 fn pct_change(&self, n: Self) -> Self {
734 self.inner.clone().pct_change(n.inner).into()
735 }
736
737 fn skew(&self, bias: bool) -> Self {
738 self.inner.clone().skew(bias).into()
739 }
740 fn kurtosis(&self, fisher: bool, bias: bool) -> Self {
741 self.inner.clone().kurtosis(fisher, bias).into()
742 }
743
744 #[cfg(feature = "dtype-array")]
745 fn reshape(&self, dims: Vec<i64>) -> Self {
746 self.inner.clone().reshape(&dims).into()
747 }
748
749 fn to_physical(&self) -> Self {
750 self.inner.clone().to_physical().into()
751 }
752
753 #[pyo3(signature = (seed))]
754 fn shuffle(&self, seed: Option<u64>) -> Self {
755 self.inner.clone().shuffle(seed).into()
756 }
757
758 #[pyo3(signature = (n, with_replacement, shuffle, seed))]
759 fn sample_n(&self, n: Self, with_replacement: bool, shuffle: bool, seed: Option<u64>) -> Self {
760 self.inner
761 .clone()
762 .sample_n(n.inner, with_replacement, shuffle, seed)
763 .into()
764 }
765
766 #[pyo3(signature = (frac, with_replacement, shuffle, seed))]
767 fn sample_frac(
768 &self,
769 frac: Self,
770 with_replacement: bool,
771 shuffle: bool,
772 seed: Option<u64>,
773 ) -> Self {
774 self.inner
775 .clone()
776 .sample_frac(frac.inner, with_replacement, shuffle, seed)
777 .into()
778 }
779
780 fn ewm_mean(&self, alpha: f64, adjust: bool, min_periods: usize, ignore_nulls: bool) -> Self {
781 let options = EWMOptions {
782 alpha,
783 adjust,
784 bias: false,
785 min_periods,
786 ignore_nulls,
787 };
788 self.inner.clone().ewm_mean(options).into()
789 }
790 fn ewm_mean_by(&self, times: PyExpr, half_life: &str) -> PyResult<Self> {
791 let half_life = Duration::try_parse(half_life).map_err(PyPolarsErr::from)?;
792 Ok(self
793 .inner
794 .clone()
795 .ewm_mean_by(times.inner, half_life)
796 .into())
797 }
798
799 fn ewm_std(
800 &self,
801 alpha: f64,
802 adjust: bool,
803 bias: bool,
804 min_periods: usize,
805 ignore_nulls: bool,
806 ) -> Self {
807 let options = EWMOptions {
808 alpha,
809 adjust,
810 bias,
811 min_periods,
812 ignore_nulls,
813 };
814 self.inner.clone().ewm_std(options).into()
815 }
816 fn ewm_var(
817 &self,
818 alpha: f64,
819 adjust: bool,
820 bias: bool,
821 min_periods: usize,
822 ignore_nulls: bool,
823 ) -> Self {
824 let options = EWMOptions {
825 alpha,
826 adjust,
827 bias,
828 min_periods,
829 ignore_nulls,
830 };
831 self.inner.clone().ewm_var(options).into()
832 }
833 fn extend_constant(&self, value: PyExpr, n: PyExpr) -> Self {
834 self.inner
835 .clone()
836 .extend_constant(value.inner, n.inner)
837 .into()
838 }
839
840 fn any(&self, ignore_nulls: bool) -> Self {
841 self.inner.clone().any(ignore_nulls).into()
842 }
843 fn all(&self, ignore_nulls: bool) -> Self {
844 self.inner.clone().all(ignore_nulls).into()
845 }
846
847 fn log(&self, base: PyExpr) -> Self {
848 self.inner.clone().log(base.inner).into()
849 }
850
851 fn log1p(&self) -> Self {
852 self.inner.clone().log1p().into()
853 }
854
855 fn exp(&self) -> Self {
856 self.inner.clone().exp().into()
857 }
858
859 fn entropy(&self, base: f64, normalize: bool) -> Self {
860 self.inner.clone().entropy(base, normalize).into()
861 }
862 fn hash(&self, seed: u64, seed_1: u64, seed_2: u64, seed_3: u64) -> Self {
863 self.inner.clone().hash(seed, seed_1, seed_2, seed_3).into()
864 }
865 fn set_sorted_flag(&self, descending: bool) -> Self {
866 let is_sorted = if descending {
867 IsSorted::Descending
868 } else {
869 IsSorted::Ascending
870 };
871 self.inner.clone().set_sorted_flag(is_sorted).into()
872 }
873
874 fn replace(&self, old: PyExpr, new: PyExpr) -> Self {
875 self.inner.clone().replace(old.inner, new.inner).into()
876 }
877
878 #[pyo3(signature = (old, new, default, return_dtype))]
879 fn replace_strict(
880 &self,
881 old: PyExpr,
882 new: PyExpr,
883 default: Option<PyExpr>,
884 return_dtype: Option<PyDataTypeExpr>,
885 ) -> Self {
886 self.inner
887 .clone()
888 .replace_strict(
889 old.inner,
890 new.inner,
891 default.map(|e| e.inner),
892 return_dtype.map(|dt| dt.inner),
893 )
894 .into()
895 }
896
897 #[cfg(feature = "hist")]
898 #[pyo3(signature = (bins, bin_count, include_category, include_breakpoint))]
899 fn hist(
900 &self,
901 bins: Option<PyExpr>,
902 bin_count: Option<usize>,
903 include_category: bool,
904 include_breakpoint: bool,
905 ) -> Self {
906 let bins = bins.map(|e| e.inner);
907 self.inner
908 .clone()
909 .hist(bins, bin_count, include_category, include_breakpoint)
910 .into()
911 }
912
913 #[pyo3(signature = (schema))]
914 fn skip_batch_predicate(&self, py: Python<'_>, schema: Wrap<Schema>) -> PyResult<Option<Self>> {
915 let mut aexpr_arena = Arena::new();
916 py.enter_polars(|| {
917 let mut ctx = ExprToIRContext::new(&mut aexpr_arena, &schema.0);
918 ctx.allow_unknown = true;
919 let node = to_expr_ir(self.inner.clone(), &mut ctx)?.node();
920 let Some(node) = aexpr_to_skip_batch_predicate(node, &mut aexpr_arena, &schema.0)
921 else {
922 return Ok(None);
923 };
924 let skip_batch_predicate = node_to_expr(node, &aexpr_arena);
925 PolarsResult::Ok(Some(Self {
926 inner: skip_batch_predicate,
927 }))
928 })
929 }
930
931 #[staticmethod]
932 fn row_encode_unordered(exprs: Vec<Self>) -> Self {
933 Expr::n_ary(
934 FunctionExpr::RowEncode(RowEncodingVariant::Unordered),
935 exprs.into_iter().map(|e| e.inner.clone()).collect(),
936 )
937 .into()
938 }
939
940 #[staticmethod]
941 fn row_encode_ordered(
942 exprs: Vec<Self>,
943 descending: Option<Vec<bool>>,
944 nulls_last: Option<Vec<bool>>,
945 ) -> Self {
946 Expr::n_ary(
947 FunctionExpr::RowEncode(RowEncodingVariant::Ordered {
948 descending,
949 nulls_last,
950 }),
951 exprs.into_iter().map(|e| e.inner.clone()).collect(),
952 )
953 .into()
954 }
955
956 fn row_decode_unordered(&self, names: Vec<String>, datatypes: Vec<PyDataTypeExpr>) -> Self {
957 let fields = names
958 .into_iter()
959 .zip(datatypes)
960 .map(|(name, dtype)| (PlSmallStr::from_string(name), dtype.inner))
961 .collect();
962 self.inner
963 .clone()
964 .map_unary(FunctionExpr::RowDecode(
965 fields,
966 RowEncodingVariant::Unordered,
967 ))
968 .into()
969 }
970
971 fn row_decode_ordered(
972 &self,
973 names: Vec<String>,
974 datatypes: Vec<PyDataTypeExpr>,
975 descending: Option<Vec<bool>>,
976 nulls_last: Option<Vec<bool>>,
977 ) -> Self {
978 let fields = names
979 .into_iter()
980 .zip(datatypes)
981 .map(|(name, dtype)| (PlSmallStr::from_string(name), dtype.inner))
982 .collect::<Vec<_>>();
983 self.inner
984 .clone()
985 .map_unary(FunctionExpr::RowDecode(
986 fields,
987 RowEncodingVariant::Ordered {
988 descending,
989 nulls_last,
990 },
991 ))
992 .into()
993 }
994
995 #[allow(clippy::wrong_self_convention)]
996 fn into_selector(&self) -> PyResult<PySelector> {
997 Ok(self
998 .inner
999 .clone()
1000 .into_selector()
1001 .ok_or_else(
1002 || polars_err!(InvalidOperation: "expr `{}` is not a selector", &self.inner),
1003 )
1004 .map_err(PyPolarsErr::from)?
1005 .into())
1006 }
1007
1008 #[staticmethod]
1009 fn new_selector(selector: PySelector) -> Self {
1010 Expr::Selector(selector.inner).into()
1011 }
1012}