polars_core/chunked_array/ops/
mod.rs1use arrow::offset::OffsetsBuffer;
3use polars_compute::rolling::QuantileMethod;
4
5use crate::prelude::*;
6
7pub(crate) mod aggregate;
8pub(crate) mod any_value;
9pub(crate) mod append;
10mod apply;
11#[cfg(feature = "approx_unique")]
12mod approx_n_unique;
13pub mod arity;
14mod bit_repr;
15mod bits;
16#[cfg(feature = "bitwise")]
17mod bitwise_reduce;
18pub(crate) mod chunkops;
19pub(crate) mod compare_inner;
20#[cfg(feature = "dtype-decimal")]
21mod decimal;
22pub(crate) mod downcast;
23pub(crate) mod explode;
24mod explode_and_offsets;
25mod extend;
26pub mod fill_null;
27mod filter;
28pub mod float_sorted_arg_max;
29mod for_each;
30pub mod full;
31pub mod gather;
32pub(crate) mod nulls;
33mod reverse;
34#[cfg(feature = "rolling_window")]
35pub(crate) mod rolling_window;
36pub mod row_encode;
37pub mod search_sorted;
38mod set;
39mod shift;
40pub mod sort;
41#[cfg(feature = "algorithm_group_by")]
42pub(crate) mod unique;
43#[cfg(feature = "zip_with")]
44pub mod zip;
45
46pub use chunkops::_set_check_length;
47#[cfg(feature = "serde-lazy")]
48use serde::{Deserialize, Serialize};
49pub use sort::options::*;
50
51use crate::chunked_array::cast::CastOptions;
52use crate::series::{BitRepr, IsSorted};
53#[cfg(feature = "reinterpret")]
54pub trait Reinterpret {
55 fn reinterpret_signed(&self) -> Series {
56 unimplemented!()
57 }
58
59 fn reinterpret_unsigned(&self) -> Series {
60 unimplemented!()
61 }
62}
63
64pub(crate) trait ToBitRepr {
68 fn to_bit_repr(&self) -> BitRepr;
69}
70
71pub trait ChunkAnyValue {
72 unsafe fn get_any_value_unchecked(&self, index: usize) -> AnyValue;
78
79 fn get_any_value(&self, index: usize) -> PolarsResult<AnyValue>;
81}
82
83pub trait ChunkExplode {
85 fn explode(&self) -> PolarsResult<Series> {
86 self.explode_and_offsets().map(|t| t.0)
87 }
88 fn offsets(&self) -> PolarsResult<OffsetsBuffer<i64>>;
89 fn explode_and_offsets(&self) -> PolarsResult<(Series, OffsetsBuffer<i64>)>;
90}
91
92pub trait ChunkBytes {
93 fn to_byte_slices(&self) -> Vec<&[u8]>;
94}
95
96#[cfg(feature = "rolling_window")]
100pub trait ChunkRollApply: AsRefDataType {
101 fn rolling_map(
102 &self,
103 _f: &dyn Fn(&Series) -> Series,
104 _options: RollingOptionsFixedWindow,
105 ) -> PolarsResult<Series>
106 where
107 Self: Sized,
108 {
109 polars_bail!(opq = rolling_map, self.as_ref_dtype());
110 }
111}
112
113pub trait ChunkTake<Idx: ?Sized>: ChunkTakeUnchecked<Idx> {
114 fn take(&self, indices: &Idx) -> PolarsResult<Self>
116 where
117 Self: Sized;
118}
119
120pub trait ChunkTakeUnchecked<Idx: ?Sized> {
121 unsafe fn take_unchecked(&self, indices: &Idx) -> Self;
126}
127
128pub trait ChunkSet<'a, A, B> {
133 fn scatter_single<I: IntoIterator<Item = IdxSize>>(
145 &'a self,
146 idx: I,
147 opt_value: Option<A>,
148 ) -> PolarsResult<Self>
149 where
150 Self: Sized;
151
152 fn scatter_with<I: IntoIterator<Item = IdxSize>, F>(
164 &'a self,
165 idx: I,
166 f: F,
167 ) -> PolarsResult<Self>
168 where
169 Self: Sized,
170 F: Fn(Option<A>) -> Option<B>;
171 fn set(&'a self, mask: &BooleanChunked, opt_value: Option<A>) -> PolarsResult<Self>
183 where
184 Self: Sized;
185}
186
187pub trait ChunkCast {
189 fn cast(&self, dtype: &DataType) -> PolarsResult<Series> {
191 self.cast_with_options(dtype, CastOptions::NonStrict)
192 }
193
194 fn cast_with_options(&self, dtype: &DataType, options: CastOptions) -> PolarsResult<Series>;
196
197 unsafe fn cast_unchecked(&self, dtype: &DataType) -> PolarsResult<Series>;
203}
204
205pub trait ChunkApply<'a, T> {
208 type FuncRet;
209
210 #[must_use]
224 fn apply_values<F>(&'a self, f: F) -> Self
225 where
226 F: Fn(T) -> Self::FuncRet + Copy;
227
228 #[must_use]
230 fn apply<F>(&'a self, f: F) -> Self
231 where
232 F: Fn(Option<T>) -> Option<Self::FuncRet> + Copy;
233
234 fn apply_to_slice<F, S>(&'a self, f: F, slice: &mut [S])
236 where
238 F: Fn(Option<T>, &S) -> S;
239}
240
241pub trait ChunkAgg<T> {
243 fn sum(&self) -> Option<T> {
247 None
248 }
249
250 fn _sum_as_f64(&self) -> f64;
251
252 fn min(&self) -> Option<T> {
253 None
254 }
255
256 fn max(&self) -> Option<T> {
259 None
260 }
261
262 fn min_max(&self) -> Option<(T, T)> {
263 Some((self.min()?, self.max()?))
264 }
265
266 fn mean(&self) -> Option<f64> {
269 None
270 }
271}
272
273pub trait ChunkQuantile<T> {
275 fn median(&self) -> Option<T> {
278 None
279 }
280 fn quantile(&self, _quantile: f64, _method: QuantileMethod) -> PolarsResult<Option<T>> {
283 Ok(None)
284 }
285}
286
287pub trait ChunkVar {
289 fn var(&self, _ddof: u8) -> Option<f64> {
291 None
292 }
293
294 fn std(&self, _ddof: u8) -> Option<f64> {
296 None
297 }
298}
299
300#[cfg(feature = "bitwise")]
302pub trait ChunkBitwiseReduce {
303 type Physical;
304
305 fn and_reduce(&self) -> Option<Self::Physical>;
306 fn or_reduce(&self) -> Option<Self::Physical>;
307 fn xor_reduce(&self) -> Option<Self::Physical>;
308}
309
310pub trait ChunkCompareEq<Rhs> {
327 type Item;
328
329 fn equal(&self, rhs: Rhs) -> Self::Item;
331
332 fn equal_missing(&self, rhs: Rhs) -> Self::Item;
334
335 fn not_equal(&self, rhs: Rhs) -> Self::Item;
337
338 fn not_equal_missing(&self, rhs: Rhs) -> Self::Item;
340}
341
342pub trait ChunkCompareIneq<Rhs> {
345 type Item;
346
347 fn gt(&self, rhs: Rhs) -> Self::Item;
349
350 fn gt_eq(&self, rhs: Rhs) -> Self::Item;
352
353 fn lt(&self, rhs: Rhs) -> Self::Item;
355
356 fn lt_eq(&self, rhs: Rhs) -> Self::Item;
358}
359
360pub trait ChunkUnique {
362 fn unique(&self) -> PolarsResult<Self>
365 where
366 Self: Sized;
367
368 fn arg_unique(&self) -> PolarsResult<IdxCa>;
371
372 fn n_unique(&self) -> PolarsResult<usize> {
374 self.arg_unique().map(|v| v.len())
375 }
376}
377
378#[cfg(feature = "approx_unique")]
379pub trait ChunkApproxNUnique {
380 fn approx_n_unique(&self) -> IdxSize;
381}
382
383pub trait ChunkSort<T: PolarsDataType> {
385 #[allow(unused_variables)]
386 fn sort_with(&self, options: SortOptions) -> ChunkedArray<T>;
387
388 fn sort(&self, descending: bool) -> ChunkedArray<T>;
390
391 fn arg_sort(&self, options: SortOptions) -> IdxCa;
393
394 #[allow(unused_variables)]
396 fn arg_sort_multiple(
397 &self,
398 by: &[Column],
399 _options: &SortMultipleOptions,
400 ) -> PolarsResult<IdxCa> {
401 polars_bail!(opq = arg_sort_multiple, T::get_dtype());
402 }
403}
404
405pub type FillNullLimit = Option<IdxSize>;
406
407#[derive(Copy, Clone, Debug, PartialEq, Hash)]
408#[cfg_attr(feature = "serde-lazy", derive(Serialize, Deserialize))]
409pub enum FillNullStrategy {
410 Backward(FillNullLimit),
412 Forward(FillNullLimit),
414 Mean,
416 Min,
418 Max,
420 Zero,
422 One,
424 MaxBound,
426 MinBound,
428}
429
430impl FillNullStrategy {
431 pub fn is_elementwise(&self) -> bool {
432 matches!(self, Self::One | Self::Zero)
433 }
434}
435
436pub trait ChunkFillNullValue<T> {
438 fn fill_null_with_values(&self, value: T) -> PolarsResult<Self>
440 where
441 Self: Sized;
442}
443
444pub trait ChunkFull<T> {
446 fn full(name: PlSmallStr, value: T, length: usize) -> Self
448 where
449 Self: Sized;
450}
451
452pub trait ChunkFullNull {
453 fn full_null(_name: PlSmallStr, _length: usize) -> Self
454 where
455 Self: Sized;
456}
457
458pub trait ChunkReverse {
460 fn reverse(&self) -> Self;
462}
463
464pub trait ChunkFilter<T: PolarsDataType> {
466 fn filter(&self, filter: &BooleanChunked) -> PolarsResult<ChunkedArray<T>>
477 where
478 Self: Sized;
479}
480
481pub trait ChunkExpandAtIndex<T: PolarsDataType> {
483 fn new_from_index(&self, index: usize, length: usize) -> ChunkedArray<T>;
485}
486
487macro_rules! impl_chunk_expand {
488 ($self:ident, $length:ident, $index:ident) => {{
489 if $self.is_empty() {
490 return $self.clone();
491 }
492 let opt_val = $self.get($index);
493 match opt_val {
494 Some(val) => ChunkedArray::full($self.name().clone(), val, $length),
495 None => ChunkedArray::full_null($self.name().clone(), $length),
496 }
497 }};
498}
499
500impl<T: PolarsNumericType> ChunkExpandAtIndex<T> for ChunkedArray<T>
501where
502 ChunkedArray<T>: ChunkFull<T::Native>,
503{
504 fn new_from_index(&self, index: usize, length: usize) -> ChunkedArray<T> {
505 let mut out = impl_chunk_expand!(self, length, index);
506 out.set_sorted_flag(IsSorted::Ascending);
507 out
508 }
509}
510
511impl ChunkExpandAtIndex<BooleanType> for BooleanChunked {
512 fn new_from_index(&self, index: usize, length: usize) -> BooleanChunked {
513 let mut out = impl_chunk_expand!(self, length, index);
514 out.set_sorted_flag(IsSorted::Ascending);
515 out
516 }
517}
518
519impl ChunkExpandAtIndex<StringType> for StringChunked {
520 fn new_from_index(&self, index: usize, length: usize) -> StringChunked {
521 let mut out = impl_chunk_expand!(self, length, index);
522 out.set_sorted_flag(IsSorted::Ascending);
523 out
524 }
525}
526
527impl ChunkExpandAtIndex<BinaryType> for BinaryChunked {
528 fn new_from_index(&self, index: usize, length: usize) -> BinaryChunked {
529 let mut out = impl_chunk_expand!(self, length, index);
530 out.set_sorted_flag(IsSorted::Ascending);
531 out
532 }
533}
534
535impl ChunkExpandAtIndex<BinaryOffsetType> for BinaryOffsetChunked {
536 fn new_from_index(&self, index: usize, length: usize) -> BinaryOffsetChunked {
537 let mut out = impl_chunk_expand!(self, length, index);
538 out.set_sorted_flag(IsSorted::Ascending);
539 out
540 }
541}
542
543impl ChunkExpandAtIndex<ListType> for ListChunked {
544 fn new_from_index(&self, index: usize, length: usize) -> ListChunked {
545 let opt_val = self.get_as_series(index);
546 match opt_val {
547 Some(val) => {
548 let mut ca = ListChunked::full(self.name().clone(), &val, length);
549 unsafe { ca.to_logical(self.inner_dtype().clone()) };
550 ca
551 },
552 None => {
553 ListChunked::full_null_with_dtype(self.name().clone(), length, self.inner_dtype())
554 },
555 }
556 }
557}
558
559#[cfg(feature = "dtype-struct")]
560impl ChunkExpandAtIndex<StructType> for StructChunked {
561 fn new_from_index(&self, index: usize, length: usize) -> ChunkedArray<StructType> {
562 let (chunk_idx, idx) = self.index_to_chunked_index(index);
563 let chunk = self.downcast_chunks().get(chunk_idx).unwrap();
564 let chunk = if chunk.is_null(idx) {
565 new_null_array(chunk.dtype().clone(), length)
566 } else {
567 let values = chunk
568 .values()
569 .iter()
570 .map(|arr| {
571 let s = Series::try_from((PlSmallStr::EMPTY, arr.clone())).unwrap();
572 let s = s.new_from_index(idx, length);
573 s.chunks()[0].clone()
574 })
575 .collect::<Vec<_>>();
576
577 StructArray::new(chunk.dtype().clone(), length, values, None).boxed()
578 };
579
580 unsafe { self.copy_with_chunks(vec![chunk]) }
582 }
583}
584
585#[cfg(feature = "dtype-array")]
586impl ChunkExpandAtIndex<FixedSizeListType> for ArrayChunked {
587 fn new_from_index(&self, index: usize, length: usize) -> ArrayChunked {
588 let opt_val = self.get_as_series(index);
589 match opt_val {
590 Some(val) => {
591 let mut ca = ArrayChunked::full(self.name().clone(), &val, length);
592 unsafe { ca.to_logical(self.inner_dtype().clone()) };
593 ca
594 },
595 None => ArrayChunked::full_null_with_dtype(
596 self.name().clone(),
597 length,
598 self.inner_dtype(),
599 self.width(),
600 ),
601 }
602 }
603}
604
605#[cfg(feature = "object")]
606impl<T: PolarsObject> ChunkExpandAtIndex<ObjectType<T>> for ObjectChunked<T> {
607 fn new_from_index(&self, index: usize, length: usize) -> ObjectChunked<T> {
608 let opt_val = self.get(index);
609 match opt_val {
610 Some(val) => ObjectChunked::<T>::full(self.name().clone(), val.clone(), length),
611 None => ObjectChunked::<T>::full_null(self.name().clone(), length),
612 }
613 }
614}
615
616pub trait ChunkShiftFill<T: PolarsDataType, V> {
618 fn shift_and_fill(&self, periods: i64, fill_value: V) -> ChunkedArray<T>;
621}
622
623pub trait ChunkShift<T: PolarsDataType> {
624 fn shift(&self, periods: i64) -> ChunkedArray<T>;
625}
626
627pub trait ChunkZip<T: PolarsDataType> {
629 fn zip_with(
632 &self,
633 mask: &BooleanChunked,
634 other: &ChunkedArray<T>,
635 ) -> PolarsResult<ChunkedArray<T>>;
636}
637
638pub trait ChunkApplyKernel<A: Array> {
640 #[must_use]
642 fn apply_kernel(&self, f: &dyn Fn(&A) -> ArrayRef) -> Self;
643
644 fn apply_kernel_cast<S>(&self, f: &dyn Fn(&A) -> ArrayRef) -> ChunkedArray<S>
646 where
647 S: PolarsDataType;
648}
649
650#[cfg(feature = "is_first_distinct")]
651pub trait IsFirstDistinct<T: PolarsDataType> {
653 fn is_first_distinct(&self) -> PolarsResult<BooleanChunked> {
654 polars_bail!(opq = is_first_distinct, T::get_dtype());
655 }
656}
657
658#[cfg(feature = "is_last_distinct")]
659pub trait IsLastDistinct<T: PolarsDataType> {
661 fn is_last_distinct(&self) -> PolarsResult<BooleanChunked> {
662 polars_bail!(opq = is_last_distinct, T::get_dtype());
663 }
664}