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