1use std::any::Any;
2use std::borrow::Cow;
3
4use arrow::bitmap::{Bitmap, BitmapBuilder};
5use polars_compute::rolling::QuantileMethod;
6
7use crate::chunked_array::cast::CastOptions;
8#[cfg(feature = "object")]
9use crate::chunked_array::object::PolarsObjectSafe;
10use crate::prelude::*;
11use crate::utils::{first_non_null, last_non_null};
12
13#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
14pub enum IsSorted {
15 Ascending,
16 Descending,
17 Not,
18}
19
20impl IsSorted {
21 pub fn reverse(self) -> Self {
22 use IsSorted::*;
23 match self {
24 Ascending => Descending,
25 Descending => Ascending,
26 Not => Not,
27 }
28 }
29}
30
31pub enum BitRepr {
32 U8(UInt8Chunked),
33 U16(UInt16Chunked),
34 U32(UInt32Chunked),
35 U64(UInt64Chunked),
36 #[cfg(feature = "dtype-u128")]
37 U128(UInt128Chunked),
38}
39
40pub(crate) mod private {
41 use polars_utils::aliases::PlSeedableRandomStateQuality;
42
43 use super::*;
44 use crate::chunked_array::flags::StatisticsFlags;
45 use crate::chunked_array::ops::compare_inner::{TotalEqInner, TotalOrdInner};
46
47 pub trait PrivateSeriesNumeric {
48 fn bit_repr(&self) -> Option<BitRepr>;
52 }
53
54 pub trait PrivateSeries {
55 #[cfg(feature = "object")]
56 fn get_list_builder(
57 &self,
58 _name: PlSmallStr,
59 _values_capacity: usize,
60 _list_capacity: usize,
61 ) -> Box<dyn ListBuilderTrait> {
62 invalid_operation_panic!(get_list_builder, self)
63 }
64
65 fn _field(&self) -> Cow<'_, Field>;
67
68 fn _dtype(&self) -> &DataType;
69
70 fn compute_len(&mut self);
71
72 fn _get_flags(&self) -> StatisticsFlags;
73
74 fn _set_flags(&mut self, flags: StatisticsFlags);
75
76 unsafe fn equal_element(
77 &self,
78 _idx_self: usize,
79 _idx_other: usize,
80 _other: &Series,
81 ) -> bool {
82 invalid_operation_panic!(equal_element, self)
83 }
84 #[expect(clippy::wrong_self_convention)]
85 fn into_total_eq_inner<'a>(&'a self) -> Box<dyn TotalEqInner + 'a>;
86 #[expect(clippy::wrong_self_convention)]
87 fn into_total_ord_inner<'a>(&'a self) -> Box<dyn TotalOrdInner + 'a>;
88
89 fn vec_hash(
90 &self,
91 _build_hasher: PlSeedableRandomStateQuality,
92 _buf: &mut Vec<u64>,
93 ) -> PolarsResult<()>;
94 fn vec_hash_combine(
95 &self,
96 _build_hasher: PlSeedableRandomStateQuality,
97 _hashes: &mut [u64],
98 ) -> PolarsResult<()>;
99
100 #[cfg(feature = "algorithm_group_by")]
104 unsafe fn agg_min(&self, groups: &GroupsType) -> Series {
105 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
106 }
107 #[cfg(feature = "algorithm_group_by")]
111 unsafe fn agg_max(&self, groups: &GroupsType) -> Series {
112 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
113 }
114 #[cfg(feature = "algorithm_group_by")]
118 unsafe fn agg_arg_min(&self, groups: &GroupsType) -> Series {
119 Series::full_null(self._field().name().clone(), groups.len(), &IDX_DTYPE)
120 }
121
122 #[cfg(feature = "algorithm_group_by")]
126 unsafe fn agg_arg_max(&self, groups: &GroupsType) -> Series {
127 Series::full_null(self._field().name().clone(), groups.len(), &IDX_DTYPE)
128 }
129
130 #[cfg(feature = "algorithm_group_by")]
133 unsafe fn agg_sum(&self, groups: &GroupsType) -> Series {
134 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
135 }
136 #[cfg(feature = "algorithm_group_by")]
140 unsafe fn agg_std(&self, groups: &GroupsType, _ddof: u8) -> Series {
141 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
142 }
143 #[cfg(feature = "algorithm_group_by")]
147 unsafe fn agg_var(&self, groups: &GroupsType, _ddof: u8) -> Series {
148 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
149 }
150 #[cfg(feature = "algorithm_group_by")]
154 unsafe fn agg_list(&self, groups: &GroupsType) -> Series {
155 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
156 }
157
158 #[cfg(feature = "bitwise")]
162 unsafe fn agg_and(&self, groups: &GroupsType) -> Series {
163 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
164 }
165
166 #[cfg(feature = "bitwise")]
170 unsafe fn agg_or(&self, groups: &GroupsType) -> Series {
171 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
172 }
173
174 #[cfg(feature = "bitwise")]
178 unsafe fn agg_xor(&self, groups: &GroupsType) -> Series {
179 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
180 }
181
182 fn subtract(&self, _rhs: &Series) -> PolarsResult<Series> {
183 polars_bail!(opq = subtract, self._dtype());
184 }
185 fn add_to(&self, _rhs: &Series) -> PolarsResult<Series> {
186 polars_bail!(opq = add, self._dtype());
187 }
188 fn multiply(&self, _rhs: &Series) -> PolarsResult<Series> {
189 polars_bail!(opq = multiply, self._dtype());
190 }
191 fn divide(&self, _rhs: &Series) -> PolarsResult<Series> {
192 polars_bail!(opq = divide, self._dtype());
193 }
194 fn remainder(&self, _rhs: &Series) -> PolarsResult<Series> {
195 polars_bail!(opq = remainder, self._dtype());
196 }
197 #[cfg(feature = "algorithm_group_by")]
198 fn group_tuples(&self, _multithreaded: bool, _sorted: bool) -> PolarsResult<GroupsType> {
199 polars_bail!(opq = group_tuples, self._dtype());
200 }
201 #[cfg(feature = "zip_with")]
202 fn zip_with_same_type(
203 &self,
204 _mask: &BooleanChunked,
205 _other: &Series,
206 ) -> PolarsResult<Series> {
207 polars_bail!(opq = zip_with_same_type, self._dtype());
208 }
209
210 #[allow(unused_variables)]
211 fn arg_sort_multiple(
212 &self,
213 by: &[Column],
214 _options: &SortMultipleOptions,
215 ) -> PolarsResult<IdxCa> {
216 polars_bail!(opq = arg_sort_multiple, self._dtype());
217 }
218 }
219}
220
221pub trait SeriesTrait:
222 Send + Sync + private::PrivateSeries + private::PrivateSeriesNumeric
223{
224 fn rename(&mut self, name: PlSmallStr);
226
227 fn chunk_lengths(&self) -> ChunkLenIter<'_>;
229
230 fn name(&self) -> &PlSmallStr;
232
233 fn field(&self) -> Cow<'_, Field> {
235 self._field()
236 }
237
238 fn dtype(&self) -> &DataType {
240 self._dtype()
241 }
242
243 fn chunks(&self) -> &Vec<ArrayRef>;
245
246 unsafe fn chunks_mut(&mut self) -> &mut Vec<ArrayRef>;
251
252 fn n_chunks(&self) -> usize {
254 self.chunks().len()
255 }
256
257 fn shrink_to_fit(&mut self) {
259 }
261
262 fn limit(&self, num_elements: usize) -> Series {
264 self.slice(0, num_elements)
265 }
266
267 fn slice(&self, _offset: i64, _length: usize) -> Series;
272
273 fn split_at(&self, _offset: i64) -> (Series, Series);
278
279 fn append(&mut self, other: &Series) -> PolarsResult<()>;
280 fn append_owned(&mut self, other: Series) -> PolarsResult<()>;
281
282 #[doc(hidden)]
283 fn extend(&mut self, _other: &Series) -> PolarsResult<()>;
284
285 fn filter(&self, _filter: &BooleanChunked) -> PolarsResult<Series>;
287
288 fn take(&self, _indices: &IdxCa) -> PolarsResult<Series>;
294
295 unsafe fn take_unchecked(&self, _idx: &IdxCa) -> Series;
302
303 fn take_slice(&self, _indices: &[IdxSize]) -> PolarsResult<Series>;
307
308 unsafe fn take_slice_unchecked(&self, _idx: &[IdxSize]) -> Series;
313
314 fn len(&self) -> usize;
316
317 fn is_empty(&self) -> bool {
319 self.len() == 0
320 }
321
322 fn rechunk(&self) -> Series;
324
325 fn rechunk_validity(&self) -> Option<Bitmap> {
326 if self.chunks().len() == 1 {
327 return self.chunks()[0].validity().cloned();
328 }
329
330 if !self.has_nulls() || self.is_empty() {
331 return None;
332 }
333
334 let mut bm = BitmapBuilder::with_capacity(self.len());
335 for arr in self.chunks() {
336 if let Some(v) = arr.validity() {
337 bm.extend_from_bitmap(v);
338 } else {
339 bm.extend_constant(arr.len(), true);
340 }
341 }
342 bm.into_opt_validity()
343 }
344
345 fn drop_nulls(&self) -> Series {
347 if self.null_count() == 0 {
348 Series(self.clone_inner())
349 } else {
350 self.filter(&self.is_not_null()).unwrap()
351 }
352 }
353
354 fn _sum_as_f64(&self) -> f64 {
356 invalid_operation_panic!(_sum_as_f64, self)
357 }
358
359 fn mean(&self) -> Option<f64> {
362 None
363 }
364
365 fn std(&self, _ddof: u8) -> Option<f64> {
368 None
369 }
370
371 fn var(&self, _ddof: u8) -> Option<f64> {
374 None
375 }
376
377 fn median(&self) -> Option<f64> {
380 None
381 }
382
383 fn new_from_index(&self, _index: usize, _length: usize) -> Series;
394
395 fn trim_lists_to_normalized_offsets(&self) -> Option<Series> {
400 None
401 }
402
403 fn propagate_nulls(&self) -> Option<Series> {
408 None
409 }
410
411 fn deposit(&self, validity: &Bitmap) -> Series;
412
413 fn find_validity_mismatch(&self, other: &Series, idxs: &mut Vec<IdxSize>);
415
416 fn cast(&self, _dtype: &DataType, options: CastOptions) -> PolarsResult<Series>;
417
418 fn get(&self, index: usize) -> PolarsResult<AnyValue<'_>> {
421 polars_ensure!(index < self.len(), oob = index, self.len());
422 let value = unsafe { self.get_unchecked(index) };
424 Ok(value)
425 }
426
427 unsafe fn get_unchecked(&self, _index: usize) -> AnyValue<'_>;
435
436 fn sort_with(&self, _options: SortOptions) -> PolarsResult<Series> {
437 polars_bail!(opq = sort_with, self._dtype());
438 }
439
440 #[allow(unused)]
442 fn arg_sort(&self, options: SortOptions) -> IdxCa {
443 invalid_operation_panic!(arg_sort, self)
444 }
445
446 fn null_count(&self) -> usize;
448
449 fn has_nulls(&self) -> bool;
451
452 fn unique(&self) -> PolarsResult<Series> {
454 polars_bail!(opq = unique, self._dtype());
455 }
456
457 fn n_unique(&self) -> PolarsResult<usize> {
461 polars_bail!(opq = n_unique, self._dtype());
462 }
463
464 fn arg_unique(&self) -> PolarsResult<IdxCa> {
466 polars_bail!(opq = arg_unique, self._dtype());
467 }
468
469 fn unique_id(&self) -> PolarsResult<(IdxSize, Vec<IdxSize>)>;
473
474 fn is_null(&self) -> BooleanChunked;
476
477 fn is_not_null(&self) -> BooleanChunked;
479
480 fn reverse(&self) -> Series;
482
483 fn as_single_ptr(&mut self) -> PolarsResult<usize> {
486 polars_bail!(opq = as_single_ptr, self._dtype());
487 }
488
489 fn shift(&self, _periods: i64) -> Series;
516
517 fn sum_reduce(&self) -> PolarsResult<Scalar> {
522 polars_bail!(opq = sum, self._dtype());
523 }
524 fn max_reduce(&self) -> PolarsResult<Scalar> {
526 polars_bail!(opq = max, self._dtype());
527 }
528 fn min_reduce(&self) -> PolarsResult<Scalar> {
530 polars_bail!(opq = min, self._dtype());
531 }
532 fn median_reduce(&self) -> PolarsResult<Scalar> {
534 polars_bail!(opq = median, self._dtype());
535 }
536 fn mean_reduce(&self) -> PolarsResult<Scalar> {
538 polars_bail!(opq = mean, self._dtype());
539 }
540 fn var_reduce(&self, _ddof: u8) -> PolarsResult<Scalar> {
542 polars_bail!(opq = var, self._dtype());
543 }
544 fn std_reduce(&self, _ddof: u8) -> PolarsResult<Scalar> {
546 polars_bail!(opq = std, self._dtype());
547 }
548 fn quantile_reduce(&self, _quantile: f64, _method: QuantileMethod) -> PolarsResult<Scalar> {
550 polars_bail!(opq = quantile, self._dtype());
551 }
552 fn quantiles_reduce(
554 &self,
555 _quantiles: &[f64],
556 _method: QuantileMethod,
557 ) -> PolarsResult<Scalar> {
558 polars_bail!(opq = quantiles, self._dtype());
559 }
560 fn and_reduce(&self) -> PolarsResult<Scalar> {
562 polars_bail!(opq = and_reduce, self._dtype());
563 }
564 fn or_reduce(&self) -> PolarsResult<Scalar> {
566 polars_bail!(opq = or_reduce, self._dtype());
567 }
568 fn xor_reduce(&self) -> PolarsResult<Scalar> {
570 polars_bail!(opq = xor_reduce, self._dtype());
571 }
572
573 fn first(&self) -> Scalar {
577 let dt = self.dtype();
578 let av = self.get(0).map_or(AnyValue::Null, AnyValue::into_static);
579
580 Scalar::new(dt.clone(), av)
581 }
582
583 fn first_non_null(&self) -> Scalar {
587 let av = if self.len() == 0 {
588 AnyValue::Null
589 } else {
590 let idx = if self.has_nulls() {
591 first_non_null(self.chunks().iter().map(|c| c.as_ref())).unwrap_or(0)
592 } else {
593 0
594 };
595 self.get(idx).map_or(AnyValue::Null, AnyValue::into_static)
596 };
597 Scalar::new(self.dtype().clone(), av)
598 }
599
600 fn last(&self) -> Scalar {
604 let dt = self.dtype();
605 let av = if self.len() == 0 {
606 AnyValue::Null
607 } else {
608 unsafe { self.get_unchecked(self.len() - 1) }.into_static()
610 };
611
612 Scalar::new(dt.clone(), av)
613 }
614
615 fn last_non_null(&self) -> Scalar {
619 let n = self.len();
620 let av = if n == 0 {
621 AnyValue::Null
622 } else {
623 let idx = if self.has_nulls() {
624 last_non_null(self.chunks().iter().map(|c| c.as_ref()), n).unwrap_or(n - 1)
625 } else {
626 n - 1
627 };
628 unsafe { self.get_unchecked(idx) }.into_static()
630 };
631 Scalar::new(self.dtype().clone(), av)
632 }
633
634 #[cfg(feature = "approx_unique")]
635 fn approx_n_unique(&self) -> PolarsResult<IdxSize> {
636 polars_bail!(opq = approx_n_unique, self._dtype());
637 }
638
639 fn clone_inner(&self) -> Arc<dyn SeriesTrait>;
641
642 #[cfg(feature = "object")]
643 fn get_object(&self, _index: usize) -> Option<&dyn PolarsObjectSafe> {
645 invalid_operation_panic!(get_object, self)
646 }
647
648 #[cfg(feature = "object")]
649 unsafe fn get_object_chunked_unchecked(
654 &self,
655 _chunk: usize,
656 _index: usize,
657 ) -> Option<&dyn PolarsObjectSafe> {
658 invalid_operation_panic!(get_object_chunked_unchecked, self)
659 }
660
661 fn as_any(&self) -> &dyn Any;
664
665 fn as_any_mut(&mut self) -> &mut dyn Any;
668
669 fn as_phys_any(&self) -> &dyn Any;
672
673 fn as_arc_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
674
675 #[cfg(feature = "checked_arithmetic")]
676 fn checked_div(&self, _rhs: &Series) -> PolarsResult<Series> {
677 polars_bail!(opq = checked_div, self._dtype());
678 }
679
680 #[cfg(feature = "rolling_window")]
681 fn rolling_map(
684 &self,
685 _f: &dyn Fn(&Series) -> PolarsResult<Series>,
686 _options: RollingOptionsFixedWindow,
687 ) -> PolarsResult<Series> {
688 polars_bail!(opq = rolling_map, self._dtype());
689 }
690}
691
692impl dyn SeriesTrait + '_ {
693 pub fn unpack<T: PolarsPhysicalType>(&self) -> PolarsResult<&ChunkedArray<T>> {
694 polars_ensure!(&T::get_static_dtype() == self.dtype(), unpack);
695 Ok(self.as_ref())
696 }
697}