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