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