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 bit_repr::reinterpret;
48pub use chunkops::_set_check_length;
49pub use nesting_utils::ChunkNestingUtils;
50#[cfg(feature = "serde-lazy")]
51use serde::{Deserialize, Serialize};
52pub use sort::options::*;
53
54use crate::chunked_array::cast::CastOptions;
55use crate::series::{BitRepr, IsSorted};
56
57pub(crate) trait ToBitRepr {
61 fn to_bit_repr(&self) -> BitRepr;
62}
63
64pub trait ChunkAnyValue {
65 unsafe fn get_any_value_unchecked(&self, index: usize) -> AnyValue<'_>;
71
72 fn get_any_value(&self, index: usize) -> PolarsResult<AnyValue<'_>>;
74}
75
76pub trait ChunkAnyValueBypassValidity {
77 unsafe fn get_any_value_bypass_validity(&self, index: usize) -> AnyValue<'_>;
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
85#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
86#[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]
87pub struct ExplodeOptions {
88 pub empty_as_null: bool,
90 pub keep_nulls: bool,
92}
93
94pub trait ChunkExplode {
96 fn explode(&self, options: ExplodeOptions) -> PolarsResult<Series> {
97 self.explode_and_offsets(options).map(|t| t.0)
98 }
99 fn offsets(&self) -> PolarsResult<OffsetsBuffer<i64>>;
100 fn explode_and_offsets(
101 &self,
102 options: ExplodeOptions,
103 ) -> PolarsResult<(Series, OffsetsBuffer<i64>)>;
104}
105
106pub trait ChunkBytes {
107 fn to_byte_slices(&self) -> Vec<&[u8]>;
108}
109
110#[cfg(feature = "rolling_window")]
114pub trait ChunkRollApply: AsRefDataType {
115 fn rolling_map(
116 &self,
117 f: &dyn Fn(&Series) -> PolarsResult<Series>,
118 options: RollingOptionsFixedWindow,
119 ) -> PolarsResult<Series>
120 where
121 Self: Sized;
122}
123
124pub trait ChunkTake<Idx: ?Sized>: ChunkTakeUnchecked<Idx> {
125 fn take(&self, indices: &Idx) -> PolarsResult<Self>
127 where
128 Self: Sized;
129}
130
131pub trait ChunkTakeUnchecked<Idx: ?Sized> {
132 unsafe fn take_unchecked(&self, indices: &Idx) -> Self;
137}
138
139pub trait ChunkSet<'a, A, B> {
144 fn scatter_single<I: IntoIterator<Item = IdxSize>>(
156 &'a self,
157 idx: I,
158 opt_value: Option<A>,
159 ) -> PolarsResult<Self>
160 where
161 Self: Sized;
162
163 fn scatter_with<I: IntoIterator<Item = IdxSize>, F>(
175 &'a self,
176 idx: I,
177 f: F,
178 ) -> PolarsResult<Self>
179 where
180 Self: Sized,
181 F: Fn(Option<A>) -> Option<B>;
182 fn set(&'a self, mask: &BooleanChunked, opt_value: Option<A>) -> PolarsResult<Self>
194 where
195 Self: Sized;
196}
197
198pub trait ChunkCast {
200 fn cast(&self, dtype: &DataType) -> PolarsResult<Series> {
202 self.cast_with_options(dtype, CastOptions::NonStrict)
203 }
204
205 fn cast_with_options(&self, dtype: &DataType, options: CastOptions) -> PolarsResult<Series>;
207
208 unsafe fn cast_unchecked(&self, dtype: &DataType) -> PolarsResult<Series>;
214}
215
216pub trait ChunkApply<'a, T> {
219 type FuncRet;
220
221 #[must_use]
235 fn apply_values<F>(&'a self, f: F) -> Self
236 where
237 F: Fn(T) -> Self::FuncRet + Copy;
238
239 #[must_use]
241 fn apply<F>(&'a self, f: F) -> Self
242 where
243 F: Fn(Option<T>) -> Option<Self::FuncRet> + Copy;
244
245 fn apply_to_slice<F, S>(&'a self, f: F, slice: &mut [S])
247 where
249 F: Fn(Option<T>, &S) -> S;
250}
251
252pub trait ChunkAgg<T> {
254 fn sum(&self) -> Option<T> {
258 None
259 }
260
261 fn _sum_as_f64(&self) -> f64;
262
263 fn min(&self) -> Option<T> {
264 None
265 }
266
267 fn max(&self) -> Option<T> {
270 None
271 }
272
273 fn min_max(&self) -> Option<(T, T)> {
274 Some((self.min()?, self.max()?))
275 }
276
277 fn mean(&self) -> Option<f64> {
280 None
281 }
282}
283
284pub trait ChunkQuantile<T> {
286 fn median(&self) -> Option<T> {
289 None
290 }
291 fn quantile(&self, _quantile: f64, _method: QuantileMethod) -> PolarsResult<Option<T>> {
294 Ok(None)
295 }
296 fn quantiles(&self, quantiles: &[f64], _method: QuantileMethod) -> PolarsResult<Vec<Option<T>>>
299 where
300 T: Clone,
301 {
302 Ok(vec![None; quantiles.len()])
303 }
304}
305
306pub trait ChunkVar {
308 fn var(&self, _ddof: u8) -> Option<f64> {
310 None
311 }
312
313 fn std(&self, _ddof: u8) -> Option<f64> {
315 None
316 }
317}
318
319#[cfg(feature = "bitwise")]
321pub trait ChunkBitwiseReduce {
322 type Physical;
323
324 fn and_reduce(&self) -> Option<Self::Physical>;
325 fn or_reduce(&self) -> Option<Self::Physical>;
326 fn xor_reduce(&self) -> Option<Self::Physical>;
327}
328
329pub trait ChunkCompareEq<Rhs> {
346 type Item;
347
348 fn equal(&self, rhs: Rhs) -> Self::Item;
350
351 fn equal_missing(&self, rhs: Rhs) -> Self::Item;
353
354 fn not_equal(&self, rhs: Rhs) -> Self::Item;
356
357 fn not_equal_missing(&self, rhs: Rhs) -> Self::Item;
359}
360
361pub trait ChunkCompareIneq<Rhs> {
364 type Item;
365
366 fn gt(&self, rhs: Rhs) -> Self::Item;
368
369 fn gt_eq(&self, rhs: Rhs) -> Self::Item;
371
372 fn lt(&self, rhs: Rhs) -> Self::Item;
374
375 fn lt_eq(&self, rhs: Rhs) -> Self::Item;
377}
378
379pub trait ChunkUnique {
381 fn unique(&self) -> PolarsResult<Self>
384 where
385 Self: Sized;
386
387 fn arg_unique(&self) -> PolarsResult<IdxCa>;
390
391 fn n_unique(&self) -> PolarsResult<usize> {
393 self.arg_unique().map(|v| v.len())
394 }
395
396 fn unique_id(&self) -> PolarsResult<(IdxSize, Vec<IdxSize>)>;
400}
401
402#[cfg(feature = "approx_unique")]
403pub trait ChunkApproxNUnique {
404 fn approx_n_unique(&self) -> IdxSize;
405}
406
407pub trait ChunkSort<T: PolarsDataType> {
409 #[allow(unused_variables)]
410 fn sort_with(&self, options: SortOptions) -> ChunkedArray<T>;
411
412 fn sort(&self, descending: bool) -> ChunkedArray<T>;
414
415 fn arg_sort(&self, options: SortOptions) -> IdxCa;
417
418 #[allow(unused_variables)]
420 fn arg_sort_multiple(
421 &self,
422 by: &[Column],
423 _options: &SortMultipleOptions,
424 ) -> PolarsResult<IdxCa> {
425 polars_bail!(opq = arg_sort_multiple, T::get_static_dtype());
426 }
427}
428
429pub type FillNullLimit = Option<IdxSize>;
430
431#[derive(Copy, Clone, Debug, PartialEq, Hash)]
432#[cfg_attr(feature = "serde-lazy", derive(Serialize, Deserialize))]
433#[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]
434pub enum FillNullStrategy {
435 Backward(FillNullLimit),
437 Forward(FillNullLimit),
439 Mean,
441 Min,
443 Max,
445 Zero,
447 One,
449}
450
451impl FillNullStrategy {
452 pub fn is_elementwise(&self) -> bool {
453 matches!(self, Self::One | Self::Zero)
454 }
455}
456
457pub trait ChunkFillNullValue<T> {
459 fn fill_null_with_values(&self, value: T) -> PolarsResult<Self>
461 where
462 Self: Sized;
463}
464
465pub trait ChunkFull<T> {
467 fn full(name: PlSmallStr, value: T, length: usize) -> Self
469 where
470 Self: Sized;
471}
472
473pub trait ChunkFullNull {
474 fn full_null(_name: PlSmallStr, _length: usize) -> Self
475 where
476 Self: Sized;
477}
478
479pub trait ChunkReverse {
481 fn reverse(&self) -> Self;
483}
484
485pub trait ChunkFilter<T: PolarsDataType> {
487 fn filter(&self, filter: &BooleanChunked) -> PolarsResult<ChunkedArray<T>>
498 where
499 Self: Sized;
500}
501
502pub trait ChunkExpandAtIndex<T: PolarsDataType> {
504 fn new_from_index(&self, index: usize, length: usize) -> ChunkedArray<T>;
506}
507
508macro_rules! impl_chunk_expand {
509 ($self:ident, $length:ident, $index:ident) => {{
510 if $self.is_empty() {
511 return $self.clone();
512 }
513 let opt_val = $self.get($index);
514 match opt_val {
515 Some(val) => ChunkedArray::full($self.name().clone(), val, $length),
516 None => ChunkedArray::full_null($self.name().clone(), $length),
517 }
518 }};
519}
520
521impl<T: PolarsNumericType> ChunkExpandAtIndex<T> for ChunkedArray<T>
522where
523 ChunkedArray<T>: ChunkFull<T::Native>,
524{
525 fn new_from_index(&self, index: usize, length: usize) -> ChunkedArray<T> {
526 let mut out = impl_chunk_expand!(self, length, index);
527 out.set_sorted_flag(IsSorted::Ascending);
528 out
529 }
530}
531
532impl ChunkExpandAtIndex<BooleanType> for BooleanChunked {
533 fn new_from_index(&self, index: usize, length: usize) -> BooleanChunked {
534 let mut out = impl_chunk_expand!(self, length, index);
535 out.set_sorted_flag(IsSorted::Ascending);
536 out
537 }
538}
539
540impl ChunkExpandAtIndex<StringType> for StringChunked {
541 fn new_from_index(&self, index: usize, length: usize) -> StringChunked {
542 let mut out = impl_chunk_expand!(self, length, index);
543 out.set_sorted_flag(IsSorted::Ascending);
544 out
545 }
546}
547
548impl ChunkExpandAtIndex<BinaryType> for BinaryChunked {
549 fn new_from_index(&self, index: usize, length: usize) -> BinaryChunked {
550 let mut out = impl_chunk_expand!(self, length, index);
551 out.set_sorted_flag(IsSorted::Ascending);
552 out
553 }
554}
555
556impl ChunkExpandAtIndex<BinaryOffsetType> for BinaryOffsetChunked {
557 fn new_from_index(&self, index: usize, length: usize) -> BinaryOffsetChunked {
558 let mut out = impl_chunk_expand!(self, length, index);
559 out.set_sorted_flag(IsSorted::Ascending);
560 out
561 }
562}
563
564impl ChunkExpandAtIndex<ListType> for ListChunked {
565 fn new_from_index(&self, index: usize, length: usize) -> ListChunked {
566 let opt_val = self.get_as_series(index);
567 match opt_val {
568 Some(val) => {
569 let mut ca = ListChunked::full(self.name().clone(), &val, length);
570 unsafe { ca.to_logical(self.inner_dtype().clone()) };
571 ca
572 },
573 None => {
574 ListChunked::full_null_with_dtype(self.name().clone(), length, self.inner_dtype())
575 },
576 }
577 }
578}
579
580#[cfg(feature = "dtype-struct")]
581impl ChunkExpandAtIndex<StructType> for StructChunked {
582 fn new_from_index(&self, index: usize, length: usize) -> ChunkedArray<StructType> {
583 let (chunk_idx, idx) = self.index_to_chunked_index(index);
584 let chunk = self.downcast_chunks().get(chunk_idx).unwrap();
585 let chunk = if chunk.is_null(idx) {
586 new_null_array(chunk.dtype().clone(), length)
587 } else {
588 let values = chunk
589 .values()
590 .iter()
591 .map(|arr| {
592 let s = Series::try_from((PlSmallStr::EMPTY, arr.clone())).unwrap();
593 let s = s.new_from_index(idx, length);
594 s.chunks()[0].clone()
595 })
596 .collect::<Vec<_>>();
597
598 StructArray::new(chunk.dtype().clone(), length, values, None).boxed()
599 };
600
601 unsafe { self.copy_with_chunks(vec![chunk]) }
603 }
604}
605
606#[cfg(feature = "dtype-array")]
607impl ChunkExpandAtIndex<FixedSizeListType> for ArrayChunked {
608 fn new_from_index(&self, index: usize, length: usize) -> ArrayChunked {
609 let opt_val = self.get_as_series(index);
610 match opt_val {
611 Some(val) => {
612 let mut ca = ArrayChunked::full(self.name().clone(), &val, length);
613 unsafe { ca.to_logical(self.inner_dtype().clone()) };
614 ca
615 },
616 None => ArrayChunked::full_null_with_dtype(
617 self.name().clone(),
618 length,
619 self.inner_dtype(),
620 self.width(),
621 ),
622 }
623 }
624}
625
626#[cfg(feature = "object")]
627impl<T: PolarsObject> ChunkExpandAtIndex<ObjectType<T>> for ObjectChunked<T> {
628 fn new_from_index(&self, index: usize, length: usize) -> ObjectChunked<T> {
629 let opt_val = self.get(index);
630 match opt_val {
631 Some(val) => ObjectChunked::<T>::full(self.name().clone(), val.clone(), length),
632 None => ObjectChunked::<T>::full_null(self.name().clone(), length),
633 }
634 }
635}
636
637pub trait ChunkShiftFill<T: PolarsDataType, V> {
639 fn shift_and_fill(&self, periods: i64, fill_value: V) -> ChunkedArray<T>;
642}
643
644pub trait ChunkShift<T: PolarsDataType> {
645 fn shift(&self, periods: i64) -> ChunkedArray<T>;
646}
647
648pub trait ChunkZip<T: PolarsDataType> {
650 fn zip_with(
653 &self,
654 mask: &BooleanChunked,
655 other: &ChunkedArray<T>,
656 ) -> PolarsResult<ChunkedArray<T>>;
657}
658
659pub trait ChunkApplyKernel<A: Array> {
661 #[must_use]
663 fn apply_kernel(&self, f: &dyn Fn(&A) -> ArrayRef) -> Self;
664
665 fn apply_kernel_cast<S>(&self, f: &dyn Fn(&A) -> ArrayRef) -> ChunkedArray<S>
667 where
668 S: PolarsDataType;
669}
670
671#[cfg(feature = "is_first_distinct")]
672pub trait IsFirstDistinct<T: PolarsDataType> {
674 fn is_first_distinct(&self) -> PolarsResult<BooleanChunked> {
675 polars_bail!(opq = is_first_distinct, T::get_static_dtype());
676 }
677}
678
679#[cfg(feature = "is_last_distinct")]
680pub trait IsLastDistinct<T: PolarsDataType> {
682 fn is_last_distinct(&self) -> PolarsResult<BooleanChunked> {
683 polars_bail!(opq = is_last_distinct, T::get_static_dtype());
684 }
685}