1use polars_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;
14pub mod binning;
15mod bit_repr;
16mod bits;
17#[cfg(feature = "bitwise")]
18mod bitwise_reduce;
19pub(crate) mod chunkops;
20pub(crate) mod compare_inner;
21#[cfg(feature = "dtype-decimal")]
22mod decimal;
23pub(crate) mod downcast;
24pub(crate) mod explode;
25mod explode_and_offsets;
26mod extend;
27pub mod fill_null;
28mod filter;
29pub mod float_sorted_arg_max;
30mod for_each;
31pub mod full;
32pub mod gather;
33mod nesting_utils;
34pub(crate) mod nulls;
35mod reverse;
36#[cfg(feature = "rolling_window")]
37pub(crate) mod rolling_window;
38pub mod row_encode;
39pub mod search_sorted;
40mod set;
41mod shift;
42pub mod sort;
43#[cfg(feature = "algorithm_group_by")]
44pub(crate) mod unique;
45#[cfg(feature = "zip_with")]
46pub mod zip;
47
48pub use bit_repr::reinterpret;
49pub use chunkops::_set_check_length;
50pub use nesting_utils::ChunkNestingUtils;
51#[cfg(feature = "serde-lazy")]
52use serde::{Deserialize, Serialize};
53pub use sort::options::*;
54
55use crate::chunked_array::cast::CastOptions;
56use crate::series::{BitRepr, IsSorted};
57
58pub(crate) trait ToBitRepr {
62 fn to_bit_repr(&self) -> BitRepr;
63}
64
65pub trait ChunkAnyValue {
66 unsafe fn get_any_value_unchecked(&self, index: usize) -> AnyValue<'_>;
72
73 fn get_any_value(&self, index: usize) -> PolarsResult<AnyValue<'_>>;
75}
76
77pub trait ChunkAnyValueBypassValidity {
78 unsafe fn get_any_value_bypass_validity(&self, index: usize) -> AnyValue<'_>;
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
86#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
87#[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]
88pub struct ExplodeOptions {
89 pub empty_as_null: bool,
91 pub keep_nulls: bool,
93}
94
95pub trait ChunkExplode {
97 fn explode(&self, options: ExplodeOptions) -> PolarsResult<Series> {
98 self.explode_and_offsets(options).map(|t| t.0)
99 }
100 fn offsets(&self) -> PolarsResult<OffsetsBuffer<i64>>;
101 fn explode_and_offsets(
102 &self,
103 options: ExplodeOptions,
104 ) -> PolarsResult<(Series, OffsetsBuffer<i64>)>;
105}
106
107#[cfg(feature = "rolling_window")]
111pub trait ChunkRollApply: AsRefDataType {
112 fn rolling_map(
113 &self,
114 f: &dyn Fn(&Series) -> PolarsResult<Series>,
115 options: RollingOptionsFixedWindow,
116 ) -> PolarsResult<Series>
117 where
118 Self: Sized;
119}
120
121pub trait ChunkTake<Idx: ?Sized>: ChunkTakeUnchecked<Idx> {
122 fn take(&self, indices: &Idx) -> PolarsResult<Self>
124 where
125 Self: Sized;
126}
127
128pub trait ChunkTakeUnchecked<Idx: ?Sized> {
129 unsafe fn take_unchecked(&self, indices: &Idx) -> Self;
134}
135
136pub trait ChunkSet<'a, A, B> {
141 fn scatter_single<I: IntoIterator<Item = IdxSize>>(
153 &'a self,
154 idx: I,
155 opt_value: Option<A>,
156 ) -> PolarsResult<Self>
157 where
158 Self: Sized;
159
160 fn scatter_with<I: IntoIterator<Item = IdxSize>, F>(
172 &'a self,
173 idx: I,
174 f: F,
175 ) -> PolarsResult<Self>
176 where
177 Self: Sized,
178 F: Fn(Option<A>) -> Option<B>;
179 fn set(&'a self, mask: &BooleanChunked, opt_value: Option<A>) -> PolarsResult<Self>
191 where
192 Self: Sized;
193}
194
195pub trait ChunkCast {
197 fn cast(&self, dtype: &DataType) -> PolarsResult<Series> {
199 self.cast_with_options(dtype, CastOptions::NonStrict)
200 }
201
202 fn cast_with_options(&self, dtype: &DataType, options: CastOptions) -> PolarsResult<Series>;
204
205 unsafe fn cast_unchecked(&self, dtype: &DataType) -> PolarsResult<Series>;
211}
212
213pub trait ChunkApply<'a, T> {
216 type FuncRet;
217
218 #[must_use]
232 fn apply_values<F>(&'a self, f: F) -> Self
233 where
234 F: Fn(T) -> Self::FuncRet + Copy;
235
236 #[must_use]
238 fn apply<F>(&'a self, f: F) -> Self
239 where
240 F: Fn(Option<T>) -> Option<Self::FuncRet> + Copy;
241
242 fn apply_to_slice<F, S>(&'a self, f: F, slice: &mut [S])
244 where
246 F: Fn(Option<T>, &S) -> S;
247}
248
249pub trait ChunkAgg<T> {
251 fn sum(&self) -> Option<T> {
255 None
256 }
257
258 fn _sum_as_f64(&self) -> f64;
259
260 fn min(&self) -> Option<T> {
261 None
262 }
263
264 fn max(&self) -> Option<T> {
267 None
268 }
269
270 fn min_max(&self) -> Option<(T, T)> {
271 Some((self.min()?, self.max()?))
272 }
273
274 fn mean(&self) -> Option<f64> {
277 None
278 }
279}
280
281pub trait ChunkQuantile<T> {
283 fn median(&self) -> Option<T> {
286 None
287 }
288 fn quantile(&self, _quantile: f64, _method: QuantileMethod) -> PolarsResult<Option<T>> {
291 Ok(None)
292 }
293 fn quantiles(&self, quantiles: &[f64], _method: QuantileMethod) -> PolarsResult<Vec<Option<T>>>
296 where
297 T: Clone,
298 {
299 Ok(vec![None; quantiles.len()])
300 }
301}
302
303pub trait ChunkVar {
305 fn var(&self, _ddof: u8) -> Option<f64> {
307 None
308 }
309
310 fn std(&self, _ddof: u8) -> Option<f64> {
312 None
313 }
314}
315
316#[cfg(feature = "bitwise")]
318pub trait ChunkBitwiseReduce {
319 type Physical;
320
321 fn and_reduce(&self) -> Option<Self::Physical>;
322 fn or_reduce(&self) -> Option<Self::Physical>;
323 fn xor_reduce(&self) -> Option<Self::Physical>;
324}
325
326pub trait ChunkCompareEq<Rhs> {
343 type Item;
344
345 fn equal(&self, rhs: Rhs) -> Self::Item;
347
348 fn equal_missing(&self, rhs: Rhs) -> Self::Item;
350
351 fn not_equal(&self, rhs: Rhs) -> Self::Item;
353
354 fn not_equal_missing(&self, rhs: Rhs) -> Self::Item;
356}
357
358pub trait ChunkCompareIneq<Rhs> {
361 type Item;
362
363 fn gt(&self, rhs: Rhs) -> Self::Item;
365
366 fn gt_eq(&self, rhs: Rhs) -> Self::Item;
368
369 fn lt(&self, rhs: Rhs) -> Self::Item;
371
372 fn lt_eq(&self, rhs: Rhs) -> Self::Item;
374}
375
376pub trait ChunkUnique {
378 fn unique(&self) -> PolarsResult<Self>
381 where
382 Self: Sized;
383
384 fn arg_unique(&self) -> PolarsResult<IdxCa>;
387
388 fn n_unique(&self) -> PolarsResult<usize> {
390 self.arg_unique().map(|v| v.len())
391 }
392
393 fn unique_id(&self) -> PolarsResult<(IdxSize, Vec<IdxSize>)>;
397}
398
399#[cfg(feature = "approx_unique")]
400pub trait ChunkApproxNUnique {
401 fn approx_n_unique(&self) -> IdxSize;
402}
403
404pub trait ChunkSort<T: PolarsDataType> {
406 #[allow(unused_variables)]
407 fn sort_with(&self, options: SortOptions) -> ChunkedArray<T>;
408
409 fn sort(&self, descending: bool) -> ChunkedArray<T>;
411
412 fn arg_sort(&self, options: SortOptions) -> IdxCa;
414
415 #[allow(unused_variables)]
417 fn arg_sort_multiple(
418 &self,
419 by: &[Column],
420 _options: &SortMultipleOptions,
421 ) -> PolarsResult<IdxCa> {
422 polars_bail!(opq = arg_sort_multiple, T::get_static_dtype());
423 }
424}
425
426pub type FillNullLimit = Option<IdxSize>;
427
428#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
429#[cfg_attr(feature = "serde-lazy", derive(Serialize, Deserialize))]
430#[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]
431pub enum FillNullStrategy {
432 Backward(FillNullLimit),
434 Forward(FillNullLimit),
436 Mean,
438 Min,
440 Max,
442 Zero,
444 One,
446}
447
448impl FillNullStrategy {
449 pub fn is_elementwise(&self) -> bool {
450 matches!(self, Self::One | Self::Zero)
451 }
452}
453
454pub trait ChunkFillNullValue<T> {
456 fn fill_null_with_values(&self, value: T) -> PolarsResult<Self>
458 where
459 Self: Sized;
460}
461
462pub trait ChunkFull<T> {
464 fn full(name: PlSmallStr, value: T, length: usize) -> Self
466 where
467 Self: Sized;
468}
469
470pub trait ChunkFullNull {
471 fn full_null(_name: PlSmallStr, _length: usize) -> Self
472 where
473 Self: Sized;
474}
475
476pub trait ChunkReverse {
478 fn reverse(&self) -> Self;
480}
481
482pub trait ChunkFilter<T: PolarsDataType> {
484 fn filter(&self, filter: &BooleanChunked) -> PolarsResult<ChunkedArray<T>>
495 where
496 Self: Sized;
497}
498
499pub trait ChunkExpandAtIndex<T: PolarsDataType> {
501 fn new_from_index(&self, index: usize, length: usize) -> ChunkedArray<T>;
503}
504
505macro_rules! impl_chunk_expand {
506 ($self:ident, $length:ident, $index:ident) => {{
507 if $self.is_empty() {
508 return $self.clone();
509 }
510 let opt_val = $self.get($index);
511 match opt_val {
512 Some(val) => ChunkedArray::full($self.name().clone(), val, $length),
513 None => ChunkedArray::full_null($self.name().clone(), $length),
514 }
515 }};
516}
517
518impl<T: PolarsNumericType> ChunkExpandAtIndex<T> for ChunkedArray<T>
519where
520 ChunkedArray<T>: ChunkFull<T::Native>,
521{
522 fn new_from_index(&self, index: usize, length: usize) -> ChunkedArray<T> {
523 let mut out = impl_chunk_expand!(self, length, index);
524 out.set_sorted_flag(IsSorted::Ascending);
525 out
526 }
527}
528
529impl ChunkExpandAtIndex<BooleanType> for BooleanChunked {
530 fn new_from_index(&self, index: usize, length: usize) -> BooleanChunked {
531 let mut out = impl_chunk_expand!(self, length, index);
532 out.set_sorted_flag(IsSorted::Ascending);
533 out
534 }
535}
536
537impl ChunkExpandAtIndex<StringType> for StringChunked {
538 fn new_from_index(&self, index: usize, length: usize) -> StringChunked {
539 let mut out = impl_chunk_expand!(self, length, index);
540 out.set_sorted_flag(IsSorted::Ascending);
541 out
542 }
543}
544
545impl ChunkExpandAtIndex<BinaryType> for BinaryChunked {
546 fn new_from_index(&self, index: usize, length: usize) -> BinaryChunked {
547 let mut out = impl_chunk_expand!(self, length, index);
548 out.set_sorted_flag(IsSorted::Ascending);
549 out
550 }
551}
552
553impl ChunkExpandAtIndex<BinaryOffsetType> for BinaryOffsetChunked {
554 fn new_from_index(&self, index: usize, length: usize) -> BinaryOffsetChunked {
555 let mut out = impl_chunk_expand!(self, length, index);
556 out.set_sorted_flag(IsSorted::Ascending);
557 out
558 }
559}
560
561impl ChunkExpandAtIndex<ListType> for ListChunked {
562 fn new_from_index(&self, index: usize, length: usize) -> ListChunked {
563 let opt_val = self.get_as_series(index);
564 match opt_val {
565 Some(val) => {
566 let mut ca = ListChunked::full(self.name().clone(), &val, length);
567 unsafe { ca.to_logical(self.inner_dtype().clone()) };
568 ca
569 },
570 None => {
571 ListChunked::full_null_with_dtype(self.name().clone(), length, self.inner_dtype())
572 },
573 }
574 }
575}
576
577#[cfg(feature = "dtype-struct")]
578impl ChunkExpandAtIndex<StructType> for StructChunked {
579 fn new_from_index(&self, index: usize, length: usize) -> ChunkedArray<StructType> {
580 let (chunk_idx, idx) = self.index_to_chunked_index(index);
581 let chunk = self.downcast_chunks().get(chunk_idx).unwrap();
582 let chunk = if chunk.is_null(idx) {
583 new_null_array(chunk.dtype().clone(), length)
584 } else {
585 let values = chunk
586 .values()
587 .iter()
588 .map(|arr| {
589 let s = Series::try_from((PlSmallStr::EMPTY, arr.clone())).unwrap();
590 let s = s.new_from_index(idx, length);
591 s.chunks()[0].clone()
592 })
593 .collect::<Vec<_>>();
594
595 StructArray::new(chunk.dtype().clone(), length, values, None).boxed()
596 };
597
598 unsafe { self.copy_with_chunks(vec![chunk]) }
600 }
601}
602
603#[cfg(feature = "dtype-array")]
604impl ChunkExpandAtIndex<FixedSizeListType> for ArrayChunked {
605 fn new_from_index(&self, index: usize, length: usize) -> ArrayChunked {
606 let opt_val = self.get_as_series(index);
607 match opt_val {
608 Some(val) => {
609 let mut ca = ArrayChunked::full(self.name().clone(), &val, length);
610 unsafe { ca.to_logical(self.inner_dtype().clone()) };
611 ca
612 },
613 None => ArrayChunked::full_null_with_dtype(
614 self.name().clone(),
615 length,
616 self.inner_dtype(),
617 self.width(),
618 ),
619 }
620 }
621}
622
623#[cfg(feature = "object")]
624impl<T: PolarsObject> ChunkExpandAtIndex<ObjectType<T>> for ObjectChunked<T> {
625 fn new_from_index(&self, index: usize, length: usize) -> ObjectChunked<T> {
626 let opt_val = self.get(index);
627 match opt_val {
628 Some(val) => ObjectChunked::<T>::full(self.name().clone(), val.clone(), length),
629 None => ObjectChunked::<T>::full_null(self.name().clone(), length),
630 }
631 }
632}
633
634pub trait ChunkShiftFill<T: PolarsDataType, V> {
636 fn shift_and_fill(&self, periods: i64, fill_value: V) -> ChunkedArray<T>;
639}
640
641pub trait ChunkShift<T: PolarsDataType> {
642 fn shift(&self, periods: i64) -> ChunkedArray<T>;
643}
644
645pub trait ChunkZip<T: PolarsDataType> {
647 fn zip_with(
650 &self,
651 mask: &BooleanChunked,
652 other: &ChunkedArray<T>,
653 ) -> PolarsResult<ChunkedArray<T>>;
654}
655
656pub trait ChunkApplyKernel<A: Array> {
658 #[must_use]
660 fn apply_kernel(&self, f: &dyn Fn(&A) -> ArrayRef) -> Self;
661
662 fn apply_kernel_cast<S>(&self, f: &dyn Fn(&A) -> ArrayRef) -> ChunkedArray<S>
664 where
665 S: PolarsDataType;
666}