1use 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;
32mod nesting_utils;
33pub(crate) mod nulls;
34mod reverse;
35#[cfg(feature = "rolling_window")]
36pub(crate) mod rolling_window;
37pub mod row_encode;
38pub mod search_sorted;
39mod set;
40mod shift;
41pub mod sort;
42#[cfg(feature = "algorithm_group_by")]
43pub(crate) mod unique;
44#[cfg(feature = "zip_with")]
45pub mod zip;
46
47pub use chunkops::_set_check_length;
48pub use nesting_utils::ChunkNestingUtils;
49#[cfg(feature = "serde-lazy")]
50use serde::{Deserialize, Serialize};
51pub use sort::options::*;
52
53use crate::chunked_array::cast::CastOptions;
54use crate::series::{BitRepr, IsSorted};
55pub trait Reinterpret {
56 fn reinterpret_signed(&self) -> Series {
57 unimplemented!()
58 }
59
60 fn reinterpret_unsigned(&self) -> Series {
61 unimplemented!()
62 }
63}
64
65pub(crate) trait ToBitRepr {
69 fn to_bit_repr(&self) -> BitRepr;
70}
71
72pub trait ChunkAnyValue {
73 unsafe fn get_any_value_unchecked(&self, index: usize) -> AnyValue<'_>;
79
80 fn get_any_value(&self, index: usize) -> PolarsResult<AnyValue<'_>>;
82}
83
84pub trait ChunkAnyValueBypassValidity {
85 unsafe fn get_any_value_bypass_validity(&self, index: usize) -> AnyValue<'_>;
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
93#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
94#[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]
95pub struct ExplodeOptions {
96 pub empty_as_null: bool,
98 pub keep_nulls: bool,
100}
101
102pub trait ChunkExplode {
104 fn explode(&self, options: ExplodeOptions) -> PolarsResult<Series> {
105 self.explode_and_offsets(options).map(|t| t.0)
106 }
107 fn offsets(&self) -> PolarsResult<OffsetsBuffer<i64>>;
108 fn explode_and_offsets(
109 &self,
110 options: ExplodeOptions,
111 ) -> PolarsResult<(Series, OffsetsBuffer<i64>)>;
112}
113
114pub trait ChunkBytes {
115 fn to_byte_slices(&self) -> Vec<&[u8]>;
116}
117
118#[cfg(feature = "rolling_window")]
122pub trait ChunkRollApply: AsRefDataType {
123 fn rolling_map(
124 &self,
125 f: &dyn Fn(&Series) -> PolarsResult<Series>,
126 options: RollingOptionsFixedWindow,
127 ) -> PolarsResult<Series>
128 where
129 Self: Sized;
130}
131
132pub trait ChunkTake<Idx: ?Sized>: ChunkTakeUnchecked<Idx> {
133 fn take(&self, indices: &Idx) -> PolarsResult<Self>
135 where
136 Self: Sized;
137}
138
139pub trait ChunkTakeUnchecked<Idx: ?Sized> {
140 unsafe fn take_unchecked(&self, indices: &Idx) -> Self;
145}
146
147pub trait ChunkSet<'a, A, B> {
152 fn scatter_single<I: IntoIterator<Item = IdxSize>>(
164 &'a self,
165 idx: I,
166 opt_value: Option<A>,
167 ) -> PolarsResult<Self>
168 where
169 Self: Sized;
170
171 fn scatter_with<I: IntoIterator<Item = IdxSize>, F>(
183 &'a self,
184 idx: I,
185 f: F,
186 ) -> PolarsResult<Self>
187 where
188 Self: Sized,
189 F: Fn(Option<A>) -> Option<B>;
190 fn set(&'a self, mask: &BooleanChunked, opt_value: Option<A>) -> PolarsResult<Self>
202 where
203 Self: Sized;
204}
205
206pub trait ChunkCast {
208 fn cast(&self, dtype: &DataType) -> PolarsResult<Series> {
210 self.cast_with_options(dtype, CastOptions::NonStrict)
211 }
212
213 fn cast_with_options(&self, dtype: &DataType, options: CastOptions) -> PolarsResult<Series>;
215
216 unsafe fn cast_unchecked(&self, dtype: &DataType) -> PolarsResult<Series>;
222}
223
224pub trait ChunkApply<'a, T> {
227 type FuncRet;
228
229 #[must_use]
243 fn apply_values<F>(&'a self, f: F) -> Self
244 where
245 F: Fn(T) -> Self::FuncRet + Copy;
246
247 #[must_use]
249 fn apply<F>(&'a self, f: F) -> Self
250 where
251 F: Fn(Option<T>) -> Option<Self::FuncRet> + Copy;
252
253 fn apply_to_slice<F, S>(&'a self, f: F, slice: &mut [S])
255 where
257 F: Fn(Option<T>, &S) -> S;
258}
259
260pub trait ChunkAgg<T> {
262 fn sum(&self) -> Option<T> {
266 None
267 }
268
269 fn _sum_as_f64(&self) -> f64;
270
271 fn min(&self) -> Option<T> {
272 None
273 }
274
275 fn max(&self) -> Option<T> {
278 None
279 }
280
281 fn min_max(&self) -> Option<(T, T)> {
282 Some((self.min()?, self.max()?))
283 }
284
285 fn mean(&self) -> Option<f64> {
288 None
289 }
290}
291
292pub trait ChunkQuantile<T> {
294 fn median(&self) -> Option<T> {
297 None
298 }
299 fn quantile(&self, _quantile: f64, _method: QuantileMethod) -> PolarsResult<Option<T>> {
302 Ok(None)
303 }
304 fn quantiles(&self, quantiles: &[f64], _method: QuantileMethod) -> PolarsResult<Vec<Option<T>>>
307 where
308 T: Clone,
309 {
310 Ok(vec![None; quantiles.len()])
311 }
312}
313
314pub trait ChunkVar {
316 fn var(&self, _ddof: u8) -> Option<f64> {
318 None
319 }
320
321 fn std(&self, _ddof: u8) -> Option<f64> {
323 None
324 }
325}
326
327#[cfg(feature = "bitwise")]
329pub trait ChunkBitwiseReduce {
330 type Physical;
331
332 fn and_reduce(&self) -> Option<Self::Physical>;
333 fn or_reduce(&self) -> Option<Self::Physical>;
334 fn xor_reduce(&self) -> Option<Self::Physical>;
335}
336
337pub trait ChunkCompareEq<Rhs> {
354 type Item;
355
356 fn equal(&self, rhs: Rhs) -> Self::Item;
358
359 fn equal_missing(&self, rhs: Rhs) -> Self::Item;
361
362 fn not_equal(&self, rhs: Rhs) -> Self::Item;
364
365 fn not_equal_missing(&self, rhs: Rhs) -> Self::Item;
367}
368
369pub trait ChunkCompareIneq<Rhs> {
372 type Item;
373
374 fn gt(&self, rhs: Rhs) -> Self::Item;
376
377 fn gt_eq(&self, rhs: Rhs) -> Self::Item;
379
380 fn lt(&self, rhs: Rhs) -> Self::Item;
382
383 fn lt_eq(&self, rhs: Rhs) -> Self::Item;
385}
386
387pub trait ChunkUnique {
389 fn unique(&self) -> PolarsResult<Self>
392 where
393 Self: Sized;
394
395 fn arg_unique(&self) -> PolarsResult<IdxCa>;
398
399 fn n_unique(&self) -> PolarsResult<usize> {
401 self.arg_unique().map(|v| v.len())
402 }
403
404 fn unique_id(&self) -> PolarsResult<(IdxSize, Vec<IdxSize>)>;
408}
409
410#[cfg(feature = "approx_unique")]
411pub trait ChunkApproxNUnique {
412 fn approx_n_unique(&self) -> IdxSize;
413}
414
415pub trait ChunkSort<T: PolarsDataType> {
417 #[allow(unused_variables)]
418 fn sort_with(&self, options: SortOptions) -> ChunkedArray<T>;
419
420 fn sort(&self, descending: bool) -> ChunkedArray<T>;
422
423 fn arg_sort(&self, options: SortOptions) -> IdxCa;
425
426 #[allow(unused_variables)]
428 fn arg_sort_multiple(
429 &self,
430 by: &[Column],
431 _options: &SortMultipleOptions,
432 ) -> PolarsResult<IdxCa> {
433 polars_bail!(opq = arg_sort_multiple, T::get_static_dtype());
434 }
435}
436
437pub type FillNullLimit = Option<IdxSize>;
438
439#[derive(Copy, Clone, Debug, PartialEq, Hash)]
440#[cfg_attr(feature = "serde-lazy", derive(Serialize, Deserialize))]
441#[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]
442pub enum FillNullStrategy {
443 Backward(FillNullLimit),
445 Forward(FillNullLimit),
447 Mean,
449 Min,
451 Max,
453 Zero,
455 One,
457}
458
459impl FillNullStrategy {
460 pub fn is_elementwise(&self) -> bool {
461 matches!(self, Self::One | Self::Zero)
462 }
463}
464
465pub trait ChunkFillNullValue<T> {
467 fn fill_null_with_values(&self, value: T) -> PolarsResult<Self>
469 where
470 Self: Sized;
471}
472
473pub trait ChunkFull<T> {
475 fn full(name: PlSmallStr, value: T, length: usize) -> Self
477 where
478 Self: Sized;
479}
480
481pub trait ChunkFullNull {
482 fn full_null(_name: PlSmallStr, _length: usize) -> Self
483 where
484 Self: Sized;
485}
486
487pub trait ChunkReverse {
489 fn reverse(&self) -> Self;
491}
492
493pub trait ChunkFilter<T: PolarsDataType> {
495 fn filter(&self, filter: &BooleanChunked) -> PolarsResult<ChunkedArray<T>>
506 where
507 Self: Sized;
508}
509
510pub trait ChunkExpandAtIndex<T: PolarsDataType> {
512 fn new_from_index(&self, index: usize, length: usize) -> ChunkedArray<T>;
514}
515
516macro_rules! impl_chunk_expand {
517 ($self:ident, $length:ident, $index:ident) => {{
518 if $self.is_empty() {
519 return $self.clone();
520 }
521 let opt_val = $self.get($index);
522 match opt_val {
523 Some(val) => ChunkedArray::full($self.name().clone(), val, $length),
524 None => ChunkedArray::full_null($self.name().clone(), $length),
525 }
526 }};
527}
528
529impl<T: PolarsNumericType> ChunkExpandAtIndex<T> for ChunkedArray<T>
530where
531 ChunkedArray<T>: ChunkFull<T::Native>,
532{
533 fn new_from_index(&self, index: usize, length: usize) -> ChunkedArray<T> {
534 let mut out = impl_chunk_expand!(self, length, index);
535 out.set_sorted_flag(IsSorted::Ascending);
536 out
537 }
538}
539
540impl ChunkExpandAtIndex<BooleanType> for BooleanChunked {
541 fn new_from_index(&self, index: usize, length: usize) -> BooleanChunked {
542 let mut out = impl_chunk_expand!(self, length, index);
543 out.set_sorted_flag(IsSorted::Ascending);
544 out
545 }
546}
547
548impl ChunkExpandAtIndex<StringType> for StringChunked {
549 fn new_from_index(&self, index: usize, length: usize) -> StringChunked {
550 let mut out = impl_chunk_expand!(self, length, index);
551 out.set_sorted_flag(IsSorted::Ascending);
552 out
553 }
554}
555
556impl ChunkExpandAtIndex<BinaryType> for BinaryChunked {
557 fn new_from_index(&self, index: usize, length: usize) -> BinaryChunked {
558 let mut out = impl_chunk_expand!(self, length, index);
559 out.set_sorted_flag(IsSorted::Ascending);
560 out
561 }
562}
563
564impl ChunkExpandAtIndex<BinaryOffsetType> for BinaryOffsetChunked {
565 fn new_from_index(&self, index: usize, length: usize) -> BinaryOffsetChunked {
566 let mut out = impl_chunk_expand!(self, length, index);
567 out.set_sorted_flag(IsSorted::Ascending);
568 out
569 }
570}
571
572impl ChunkExpandAtIndex<ListType> for ListChunked {
573 fn new_from_index(&self, index: usize, length: usize) -> ListChunked {
574 let opt_val = self.get_as_series(index);
575 match opt_val {
576 Some(val) => {
577 let mut ca = ListChunked::full(self.name().clone(), &val, length);
578 unsafe { ca.to_logical(self.inner_dtype().clone()) };
579 ca
580 },
581 None => {
582 ListChunked::full_null_with_dtype(self.name().clone(), length, self.inner_dtype())
583 },
584 }
585 }
586}
587
588#[cfg(feature = "dtype-struct")]
589impl ChunkExpandAtIndex<StructType> for StructChunked {
590 fn new_from_index(&self, index: usize, length: usize) -> ChunkedArray<StructType> {
591 let (chunk_idx, idx) = self.index_to_chunked_index(index);
592 let chunk = self.downcast_chunks().get(chunk_idx).unwrap();
593 let chunk = if chunk.is_null(idx) {
594 new_null_array(chunk.dtype().clone(), length)
595 } else {
596 let values = chunk
597 .values()
598 .iter()
599 .map(|arr| {
600 let s = Series::try_from((PlSmallStr::EMPTY, arr.clone())).unwrap();
601 let s = s.new_from_index(idx, length);
602 s.chunks()[0].clone()
603 })
604 .collect::<Vec<_>>();
605
606 StructArray::new(chunk.dtype().clone(), length, values, None).boxed()
607 };
608
609 unsafe { self.copy_with_chunks(vec![chunk]) }
611 }
612}
613
614#[cfg(feature = "dtype-array")]
615impl ChunkExpandAtIndex<FixedSizeListType> for ArrayChunked {
616 fn new_from_index(&self, index: usize, length: usize) -> ArrayChunked {
617 let opt_val = self.get_as_series(index);
618 match opt_val {
619 Some(val) => {
620 let mut ca = ArrayChunked::full(self.name().clone(), &val, length);
621 unsafe { ca.to_logical(self.inner_dtype().clone()) };
622 ca
623 },
624 None => ArrayChunked::full_null_with_dtype(
625 self.name().clone(),
626 length,
627 self.inner_dtype(),
628 self.width(),
629 ),
630 }
631 }
632}
633
634#[cfg(feature = "object")]
635impl<T: PolarsObject> ChunkExpandAtIndex<ObjectType<T>> for ObjectChunked<T> {
636 fn new_from_index(&self, index: usize, length: usize) -> ObjectChunked<T> {
637 let opt_val = self.get(index);
638 match opt_val {
639 Some(val) => ObjectChunked::<T>::full(self.name().clone(), val.clone(), length),
640 None => ObjectChunked::<T>::full_null(self.name().clone(), length),
641 }
642 }
643}
644
645pub trait ChunkShiftFill<T: PolarsDataType, V> {
647 fn shift_and_fill(&self, periods: i64, fill_value: V) -> ChunkedArray<T>;
650}
651
652pub trait ChunkShift<T: PolarsDataType> {
653 fn shift(&self, periods: i64) -> ChunkedArray<T>;
654}
655
656pub trait ChunkZip<T: PolarsDataType> {
658 fn zip_with(
661 &self,
662 mask: &BooleanChunked,
663 other: &ChunkedArray<T>,
664 ) -> PolarsResult<ChunkedArray<T>>;
665}
666
667pub trait ChunkApplyKernel<A: Array> {
669 #[must_use]
671 fn apply_kernel(&self, f: &dyn Fn(&A) -> ArrayRef) -> Self;
672
673 fn apply_kernel_cast<S>(&self, f: &dyn Fn(&A) -> ArrayRef) -> ChunkedArray<S>
675 where
676 S: PolarsDataType;
677}
678
679#[cfg(feature = "is_first_distinct")]
680pub trait IsFirstDistinct<T: PolarsDataType> {
682 fn is_first_distinct(&self) -> PolarsResult<BooleanChunked> {
683 polars_bail!(opq = is_first_distinct, T::get_static_dtype());
684 }
685}
686
687#[cfg(feature = "is_last_distinct")]
688pub trait IsLastDistinct<T: PolarsDataType> {
690 fn is_last_distinct(&self) -> PolarsResult<BooleanChunked> {
691 polars_bail!(opq = is_last_distinct, T::get_static_dtype());
692 }
693}