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