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