polars_python/expr/
general.rs

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}