1use std::any::Any;
2use std::borrow::Cow;
3
4use arrow::bitmap::{Bitmap, BitmapBuilder};
5use arrow::compute::utils::combine_validities_and;
6use polars_compute::rolling::QuantileMethod;
7
8use crate::chunked_array::cast::CastOptions;
9#[cfg(feature = "object")]
10use crate::chunked_array::object::PolarsObjectSafe;
11use crate::prelude::*;
12use crate::utils::{first_non_null, last_non_null};
13
14#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
15pub enum IsSorted {
16 Ascending,
17 Descending,
18 Not,
19}
20
21impl IsSorted {
22 pub fn reverse(self) -> Self {
23 use IsSorted::*;
24 match self {
25 Ascending => Descending,
26 Descending => Ascending,
27 Not => Not,
28 }
29 }
30}
31
32pub enum BitRepr {
33 U8(UInt8Chunked),
34 U16(UInt16Chunked),
35 U32(UInt32Chunked),
36 U64(UInt64Chunked),
37 #[cfg(feature = "dtype-u128")]
38 U128(UInt128Chunked),
39}
40
41pub(crate) mod private {
42 use polars_utils::aliases::PlSeedableRandomStateQuality;
43
44 use super::*;
45 use crate::chunked_array::flags::StatisticsFlags;
46 use crate::chunked_array::ops::compare_inner::{TotalEqInner, TotalOrdInner};
47
48 pub trait PrivateSeriesNumeric {
49 fn bit_repr(&self) -> Option<BitRepr>;
53 }
54
55 pub trait PrivateSeries {
56 #[cfg(feature = "object")]
57 fn get_list_builder(
58 &self,
59 _name: PlSmallStr,
60 _values_capacity: usize,
61 _list_capacity: usize,
62 ) -> Box<dyn ListBuilderTrait> {
63 invalid_operation_panic!(get_list_builder, self)
64 }
65
66 fn _field(&self) -> Cow<'_, Field>;
68
69 fn _dtype(&self) -> &DataType;
70
71 fn compute_len(&mut self);
72
73 fn _get_flags(&self) -> StatisticsFlags;
74
75 fn _set_flags(&mut self, flags: StatisticsFlags);
76
77 #[expect(clippy::wrong_self_convention)]
78 fn into_total_eq_inner<'a>(&'a self) -> Box<dyn TotalEqInner + 'a>;
79 #[expect(clippy::wrong_self_convention)]
80 fn into_total_ord_inner<'a>(&'a self) -> Box<dyn TotalOrdInner + 'a>;
81
82 fn vec_hash(
83 &self,
84 _build_hasher: PlSeedableRandomStateQuality,
85 _buf: &mut Vec<u64>,
86 ) -> PolarsResult<()>;
87 fn vec_hash_combine(
88 &self,
89 _build_hasher: PlSeedableRandomStateQuality,
90 _hashes: &mut [u64],
91 ) -> PolarsResult<()>;
92
93 #[cfg(feature = "algorithm_group_by")]
97 unsafe fn agg_min(&self, groups: &GroupsType) -> Series {
98 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
99 }
100 #[cfg(feature = "algorithm_group_by")]
104 unsafe fn agg_max(&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_arg_min(&self, groups: &GroupsType) -> Series {
112 Series::full_null(self._field().name().clone(), groups.len(), &IDX_DTYPE)
113 }
114
115 #[cfg(feature = "algorithm_group_by")]
119 unsafe fn agg_arg_max(&self, groups: &GroupsType) -> Series {
120 Series::full_null(self._field().name().clone(), groups.len(), &IDX_DTYPE)
121 }
122
123 #[cfg(feature = "algorithm_group_by")]
126 unsafe fn agg_sum(&self, groups: &GroupsType) -> Series {
127 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
128 }
129 #[cfg(feature = "algorithm_group_by")]
133 unsafe fn agg_std(&self, groups: &GroupsType, _ddof: u8) -> Series {
134 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
135 }
136 #[cfg(feature = "algorithm_group_by")]
140 unsafe fn agg_var(&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_list(&self, groups: &GroupsType) -> Series {
148 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
149 }
150
151 #[cfg(feature = "bitwise")]
155 unsafe fn agg_and(&self, groups: &GroupsType) -> Series {
156 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
157 }
158
159 #[cfg(feature = "bitwise")]
163 unsafe fn agg_or(&self, groups: &GroupsType) -> Series {
164 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
165 }
166
167 #[cfg(feature = "bitwise")]
171 unsafe fn agg_xor(&self, groups: &GroupsType) -> Series {
172 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
173 }
174
175 fn subtract(&self, _rhs: &Series) -> PolarsResult<Series> {
176 polars_bail!(opq = subtract, self._dtype());
177 }
178 fn add_to(&self, _rhs: &Series) -> PolarsResult<Series> {
179 polars_bail!(opq = add, self._dtype());
180 }
181 fn multiply(&self, _rhs: &Series) -> PolarsResult<Series> {
182 polars_bail!(opq = multiply, self._dtype());
183 }
184 fn divide(&self, _rhs: &Series) -> PolarsResult<Series> {
185 polars_bail!(opq = divide, self._dtype());
186 }
187 fn remainder(&self, _rhs: &Series) -> PolarsResult<Series> {
188 polars_bail!(opq = remainder, self._dtype());
189 }
190 #[cfg(feature = "algorithm_group_by")]
191 fn group_tuples(&self, _multithreaded: bool, _sorted: bool) -> PolarsResult<GroupsType> {
192 polars_bail!(opq = group_tuples, self._dtype());
193 }
194 #[cfg(feature = "zip_with")]
195 fn zip_with_same_type(
196 &self,
197 _mask: &BooleanChunked,
198 _other: &Series,
199 ) -> PolarsResult<Series> {
200 polars_bail!(opq = zip_with_same_type, self._dtype());
201 }
202
203 #[allow(unused_variables)]
204 fn arg_sort_multiple(
205 &self,
206 by: &[Column],
207 _options: &SortMultipleOptions,
208 ) -> PolarsResult<IdxCa> {
209 polars_bail!(opq = arg_sort_multiple, self._dtype());
210 }
211 }
212}
213
214pub trait SeriesTrait:
215 Send + Sync + private::PrivateSeries + private::PrivateSeriesNumeric
216{
217 fn rename(&mut self, name: PlSmallStr);
219
220 fn chunk_lengths(&self) -> ChunkLenIter<'_>;
222
223 fn name(&self) -> &PlSmallStr;
225
226 fn field(&self) -> Cow<'_, Field> {
228 self._field()
229 }
230
231 fn dtype(&self) -> &DataType {
233 self._dtype()
234 }
235
236 fn chunks(&self) -> &Vec<ArrayRef>;
238
239 unsafe fn chunks_mut(&mut self) -> &mut Vec<ArrayRef>;
244
245 fn n_chunks(&self) -> usize {
247 self.chunks().len()
248 }
249
250 fn shrink_to_fit(&mut self) {
252 }
254
255 fn limit(&self, num_elements: usize) -> Series {
257 self.slice(0, num_elements)
258 }
259
260 fn slice(&self, _offset: i64, _length: usize) -> Series;
265
266 fn split_at(&self, _offset: i64) -> (Series, Series);
271
272 fn append(&mut self, other: &Series) -> PolarsResult<()>;
273 fn append_owned(&mut self, other: Series) -> PolarsResult<()>;
274
275 #[doc(hidden)]
276 fn extend(&mut self, _other: &Series) -> PolarsResult<()>;
277
278 fn filter(&self, _filter: &BooleanChunked) -> PolarsResult<Series>;
280
281 fn take(&self, _indices: &IdxCa) -> PolarsResult<Series>;
287
288 unsafe fn take_unchecked(&self, _idx: &IdxCa) -> Series;
295
296 fn take_slice(&self, _indices: &[IdxSize]) -> PolarsResult<Series>;
300
301 unsafe fn take_slice_unchecked(&self, _idx: &[IdxSize]) -> Series;
306
307 fn len(&self) -> usize;
309
310 fn is_empty(&self) -> bool {
312 self.len() == 0
313 }
314
315 fn is_full_null(&self) -> bool {
317 self.len() == self.null_count()
318 }
319
320 fn rechunk(&self) -> Series;
322
323 fn rechunk_validity(&self) -> Option<Bitmap> {
325 if self.chunks().len() == 1 {
326 return self.chunks()[0].validity().cloned();
327 }
328
329 if !self.has_nulls() || self.is_empty() {
330 return None;
331 }
332
333 let mut bm = BitmapBuilder::with_capacity(self.len());
334 for arr in self.chunks() {
335 if let Some(v) = arr.validity() {
336 bm.extend_from_bitmap(v);
337 } else {
338 bm.extend_constant(arr.len(), true);
339 }
340 }
341 bm.into_opt_validity()
342 }
343
344 fn with_validity(&self, validity: Option<Bitmap>) -> Series;
346
347 fn mask(&self, validity: &Bitmap) -> Series {
351 if validity.len() == 1 {
352 if validity.get_bit(0) {
353 Series(self.clone_inner())
354 } else {
355 Series::full_null(self._field().name().clone(), self.len(), self._dtype())
356 }
357 } else if self.len() == 1 && validity.len() != 1 {
358 self.new_from_index(0, validity.len()).mask(validity)
359 } else {
360 self.with_validity(combine_validities_and(
361 self.rechunk_validity().as_ref(),
362 Some(validity),
363 ))
364 }
365 }
366
367 fn drop_nulls(&self) -> Series {
369 if self.null_count() == 0 {
370 Series(self.clone_inner())
371 } else {
372 self.filter(&self.is_not_null()).unwrap()
373 }
374 }
375
376 fn _sum_as_f64(&self) -> f64 {
378 invalid_operation_panic!(_sum_as_f64, self)
379 }
380
381 fn mean(&self) -> Option<f64> {
384 None
385 }
386
387 fn std(&self, _ddof: u8) -> Option<f64> {
390 None
391 }
392
393 fn var(&self, _ddof: u8) -> Option<f64> {
396 None
397 }
398
399 fn median(&self) -> Option<f64> {
402 None
403 }
404
405 fn new_from_index(&self, _index: usize, _length: usize) -> Series;
416
417 fn trim_lists_to_normalized_offsets(&self) -> Option<Series> {
422 None
423 }
424
425 fn propagate_nulls(&self) -> Option<Series> {
430 None
431 }
432
433 fn deposit(&self, validity: &Bitmap) -> Series;
434
435 fn find_validity_mismatch(&self, other: &Series, idxs: &mut Vec<IdxSize>);
437
438 fn cast(&self, _dtype: &DataType, options: CastOptions) -> PolarsResult<Series>;
439
440 fn get(&self, index: usize) -> PolarsResult<AnyValue<'_>> {
443 polars_ensure!(index < self.len(), oob = index, self.len());
444 let value = unsafe { self.get_unchecked(index) };
446 Ok(value)
447 }
448
449 unsafe fn get_unchecked(&self, _index: usize) -> AnyValue<'_>;
457
458 fn sort_with(&self, _options: SortOptions) -> PolarsResult<Series> {
459 polars_bail!(opq = sort_with, self._dtype());
460 }
461
462 #[allow(unused)]
464 fn arg_sort(&self, options: SortOptions) -> IdxCa {
465 invalid_operation_panic!(arg_sort, self)
466 }
467
468 fn null_count(&self) -> usize;
470
471 fn has_nulls(&self) -> bool;
473
474 fn unique(&self) -> PolarsResult<Series> {
476 polars_bail!(opq = unique, self._dtype());
477 }
478
479 fn n_unique(&self) -> PolarsResult<usize> {
483 polars_bail!(opq = n_unique, self._dtype());
484 }
485
486 fn arg_unique(&self) -> PolarsResult<IdxCa> {
488 polars_bail!(opq = arg_unique, self._dtype());
489 }
490
491 fn unique_id(&self) -> PolarsResult<(IdxSize, Vec<IdxSize>)> {
495 polars_bail!(opq = unique_id, self._dtype());
496 }
497
498 fn is_null(&self) -> BooleanChunked;
500
501 fn is_not_null(&self) -> BooleanChunked;
503
504 fn reverse(&self) -> Series;
506
507 fn as_single_ptr(&mut self) -> PolarsResult<usize> {
510 polars_bail!(opq = as_single_ptr, self._dtype());
511 }
512
513 fn shift(&self, _periods: i64) -> Series;
540
541 fn sum_reduce(&self) -> PolarsResult<Scalar> {
546 polars_bail!(opq = sum, self._dtype());
547 }
548 fn max_reduce(&self) -> PolarsResult<Scalar> {
550 polars_bail!(opq = max, self._dtype());
551 }
552 fn min_reduce(&self) -> PolarsResult<Scalar> {
554 polars_bail!(opq = min, self._dtype());
555 }
556 fn median_reduce(&self) -> PolarsResult<Scalar> {
558 polars_bail!(opq = median, self._dtype());
559 }
560 fn mean_reduce(&self) -> PolarsResult<Scalar> {
562 polars_bail!(opq = mean, self._dtype());
563 }
564 fn var_reduce(&self, _ddof: u8) -> PolarsResult<Scalar> {
566 polars_bail!(opq = var, self._dtype());
567 }
568 fn std_reduce(&self, _ddof: u8) -> PolarsResult<Scalar> {
570 polars_bail!(opq = std, self._dtype());
571 }
572 fn quantile_reduce(&self, _quantile: f64, _method: QuantileMethod) -> PolarsResult<Scalar> {
574 polars_bail!(opq = quantile, self._dtype());
575 }
576 fn quantiles_reduce(
578 &self,
579 _quantiles: &[f64],
580 _method: QuantileMethod,
581 ) -> PolarsResult<Scalar> {
582 polars_bail!(opq = quantiles, self._dtype());
583 }
584 fn and_reduce(&self) -> PolarsResult<Scalar> {
586 polars_bail!(opq = and_reduce, self._dtype());
587 }
588 fn or_reduce(&self) -> PolarsResult<Scalar> {
590 polars_bail!(opq = or_reduce, self._dtype());
591 }
592 fn xor_reduce(&self) -> PolarsResult<Scalar> {
594 polars_bail!(opq = xor_reduce, self._dtype());
595 }
596
597 fn first(&self) -> Scalar {
601 let dt = self.dtype();
602 let av = self.get(0).map_or(AnyValue::Null, AnyValue::into_static);
603
604 Scalar::new(dt.clone(), av)
605 }
606
607 fn first_non_null(&self) -> Scalar {
611 let av = if self.len() == 0 {
612 AnyValue::Null
613 } else {
614 let idx = if self.has_nulls() {
615 first_non_null(self.chunks().iter().map(|c| c.as_ref())).unwrap_or(0)
616 } else {
617 0
618 };
619 self.get(idx).map_or(AnyValue::Null, AnyValue::into_static)
620 };
621 Scalar::new(self.dtype().clone(), av)
622 }
623
624 fn last(&self) -> Scalar {
628 let dt = self.dtype();
629 let av = if self.len() == 0 {
630 AnyValue::Null
631 } else {
632 unsafe { self.get_unchecked(self.len() - 1) }.into_static()
634 };
635
636 Scalar::new(dt.clone(), av)
637 }
638
639 fn last_non_null(&self) -> Scalar {
643 let n = self.len();
644 let av = if n == 0 {
645 AnyValue::Null
646 } else {
647 let idx = if self.has_nulls() {
648 last_non_null(self.chunks().iter().map(|c| c.as_ref()), n).unwrap_or(n - 1)
649 } else {
650 n - 1
651 };
652 unsafe { self.get_unchecked(idx) }.into_static()
654 };
655 Scalar::new(self.dtype().clone(), av)
656 }
657
658 #[cfg(feature = "approx_unique")]
659 fn approx_n_unique(&self) -> PolarsResult<IdxSize> {
660 polars_bail!(opq = approx_n_unique, self._dtype());
661 }
662
663 fn clone_inner(&self) -> Arc<dyn SeriesTrait>;
665
666 #[cfg(feature = "object")]
667 fn get_object(&self, _index: usize) -> Option<&dyn PolarsObjectSafe> {
669 invalid_operation_panic!(get_object, self)
670 }
671
672 #[cfg(feature = "object")]
673 unsafe fn get_object_chunked_unchecked(
678 &self,
679 _chunk: usize,
680 _index: usize,
681 ) -> Option<&dyn PolarsObjectSafe> {
682 invalid_operation_panic!(get_object_chunked_unchecked, self)
683 }
684
685 fn as_any(&self) -> &dyn Any;
688
689 fn as_any_mut(&mut self) -> &mut dyn Any;
692
693 fn as_phys_any(&self) -> &dyn Any;
696
697 fn as_arc_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
698
699 #[cfg(feature = "checked_arithmetic")]
700 fn checked_div(&self, _rhs: &Series) -> PolarsResult<Series> {
701 polars_bail!(opq = checked_div, self._dtype());
702 }
703
704 #[cfg(feature = "rolling_window")]
705 fn rolling_map(
708 &self,
709 _f: &dyn Fn(&Series) -> PolarsResult<Series>,
710 _options: RollingOptionsFixedWindow,
711 ) -> PolarsResult<Series> {
712 polars_bail!(opq = rolling_map, self._dtype());
713 }
714}
715
716impl dyn SeriesTrait + '_ {
717 pub fn unpack<T: PolarsPhysicalType>(&self) -> PolarsResult<&ChunkedArray<T>> {
718 polars_ensure!(&T::get_static_dtype() == self.dtype(), unpack);
719 Ok(self.as_ref())
720 }
721}