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 #[expect(clippy::wrong_self_convention)]
77 fn into_total_eq_inner<'a>(&'a self) -> Box<dyn TotalEqInner + 'a>;
78 #[expect(clippy::wrong_self_convention)]
79 fn into_total_ord_inner<'a>(&'a self) -> Box<dyn TotalOrdInner + 'a>;
80
81 fn vec_hash(
82 &self,
83 _build_hasher: PlSeedableRandomStateQuality,
84 _buf: &mut Vec<u64>,
85 ) -> PolarsResult<()>;
86 fn vec_hash_combine(
87 &self,
88 _build_hasher: PlSeedableRandomStateQuality,
89 _hashes: &mut [u64],
90 ) -> PolarsResult<()>;
91
92 #[cfg(feature = "algorithm_group_by")]
96 unsafe fn agg_min(&self, groups: &GroupsType) -> Series {
97 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
98 }
99 #[cfg(feature = "algorithm_group_by")]
103 unsafe fn agg_max(&self, groups: &GroupsType) -> Series {
104 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
105 }
106 #[cfg(feature = "algorithm_group_by")]
110 unsafe fn agg_arg_min(&self, groups: &GroupsType) -> Series {
111 Series::full_null(self._field().name().clone(), groups.len(), &IDX_DTYPE)
112 }
113
114 #[cfg(feature = "algorithm_group_by")]
118 unsafe fn agg_arg_max(&self, groups: &GroupsType) -> Series {
119 Series::full_null(self._field().name().clone(), groups.len(), &IDX_DTYPE)
120 }
121
122 #[cfg(feature = "algorithm_group_by")]
125 unsafe fn agg_sum(&self, groups: &GroupsType) -> Series {
126 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
127 }
128 #[cfg(feature = "algorithm_group_by")]
132 unsafe fn agg_std(&self, groups: &GroupsType, _ddof: u8) -> Series {
133 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
134 }
135 #[cfg(feature = "algorithm_group_by")]
139 unsafe fn agg_var(&self, groups: &GroupsType, _ddof: u8) -> Series {
140 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
141 }
142 #[cfg(feature = "algorithm_group_by")]
146 unsafe fn agg_list(&self, groups: &GroupsType) -> Series {
147 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
148 }
149
150 #[cfg(feature = "bitwise")]
154 unsafe fn agg_and(&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_or(&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_xor(&self, groups: &GroupsType) -> Series {
171 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
172 }
173
174 fn subtract(&self, _rhs: &Series) -> PolarsResult<Series> {
175 polars_bail!(opq = subtract, self._dtype());
176 }
177 fn add_to(&self, _rhs: &Series) -> PolarsResult<Series> {
178 polars_bail!(opq = add, self._dtype());
179 }
180 fn multiply(&self, _rhs: &Series) -> PolarsResult<Series> {
181 polars_bail!(opq = multiply, self._dtype());
182 }
183 fn divide(&self, _rhs: &Series) -> PolarsResult<Series> {
184 polars_bail!(opq = divide, self._dtype());
185 }
186 fn remainder(&self, _rhs: &Series) -> PolarsResult<Series> {
187 polars_bail!(opq = remainder, self._dtype());
188 }
189 #[cfg(feature = "algorithm_group_by")]
190 fn group_tuples(&self, _multithreaded: bool, _sorted: bool) -> PolarsResult<GroupsType> {
191 polars_bail!(opq = group_tuples, self._dtype());
192 }
193 #[cfg(feature = "zip_with")]
194 fn zip_with_same_type(
195 &self,
196 _mask: &BooleanChunked,
197 _other: &Series,
198 ) -> PolarsResult<Series> {
199 polars_bail!(opq = zip_with_same_type, self._dtype());
200 }
201
202 #[allow(unused_variables)]
203 fn arg_sort_multiple(
204 &self,
205 by: &[Column],
206 _options: &SortMultipleOptions,
207 ) -> PolarsResult<IdxCa> {
208 polars_bail!(opq = arg_sort_multiple, self._dtype());
209 }
210 }
211}
212
213pub trait SeriesTrait:
214 Send + Sync + private::PrivateSeries + private::PrivateSeriesNumeric
215{
216 fn rename(&mut self, name: PlSmallStr);
218
219 fn chunk_lengths(&self) -> ChunkLenIter<'_>;
221
222 fn name(&self) -> &PlSmallStr;
224
225 fn field(&self) -> Cow<'_, Field> {
227 self._field()
228 }
229
230 fn dtype(&self) -> &DataType {
232 self._dtype()
233 }
234
235 fn chunks(&self) -> &Vec<ArrayRef>;
237
238 unsafe fn chunks_mut(&mut self) -> &mut Vec<ArrayRef>;
243
244 fn n_chunks(&self) -> usize {
246 self.chunks().len()
247 }
248
249 fn shrink_to_fit(&mut self) {
251 }
253
254 fn limit(&self, num_elements: usize) -> Series {
256 self.slice(0, num_elements)
257 }
258
259 fn slice(&self, _offset: i64, _length: usize) -> Series;
264
265 fn split_at(&self, _offset: i64) -> (Series, Series);
270
271 fn append(&mut self, other: &Series) -> PolarsResult<()>;
272 fn append_owned(&mut self, other: Series) -> PolarsResult<()>;
273
274 #[doc(hidden)]
275 fn extend(&mut self, _other: &Series) -> PolarsResult<()>;
276
277 fn filter(&self, _filter: &BooleanChunked) -> PolarsResult<Series>;
279
280 fn take(&self, _indices: &IdxCa) -> PolarsResult<Series>;
286
287 unsafe fn take_unchecked(&self, _idx: &IdxCa) -> Series;
294
295 fn take_slice(&self, _indices: &[IdxSize]) -> PolarsResult<Series>;
299
300 unsafe fn take_slice_unchecked(&self, _idx: &[IdxSize]) -> Series;
305
306 fn len(&self) -> usize;
308
309 fn is_empty(&self) -> bool {
311 self.len() == 0
312 }
313
314 fn is_full_null(&self) -> bool {
316 self.len() == self.null_count()
317 }
318
319 fn rechunk(&self) -> Series;
321
322 fn rechunk_validity(&self) -> Option<Bitmap> {
324 if self.chunks().len() == 1 {
325 return self.chunks()[0].validity().cloned();
326 }
327
328 if !self.has_nulls() || self.is_empty() {
329 return None;
330 }
331
332 let mut bm = BitmapBuilder::with_capacity(self.len());
333 for arr in self.chunks() {
334 if let Some(v) = arr.validity() {
335 bm.extend_from_bitmap(v);
336 } else {
337 bm.extend_constant(arr.len(), true);
338 }
339 }
340 bm.into_opt_validity()
341 }
342
343 fn with_validity(&self, validity: Option<Bitmap>) -> Series;
345
346 fn drop_nulls(&self) -> Series {
348 if self.null_count() == 0 {
349 Series(self.clone_inner())
350 } else {
351 self.filter(&self.is_not_null()).unwrap()
352 }
353 }
354
355 fn _sum_as_f64(&self) -> f64 {
357 invalid_operation_panic!(_sum_as_f64, self)
358 }
359
360 fn mean(&self) -> Option<f64> {
363 None
364 }
365
366 fn std(&self, _ddof: u8) -> Option<f64> {
369 None
370 }
371
372 fn var(&self, _ddof: u8) -> Option<f64> {
375 None
376 }
377
378 fn median(&self) -> Option<f64> {
381 None
382 }
383
384 fn new_from_index(&self, _index: usize, _length: usize) -> Series;
395
396 fn trim_lists_to_normalized_offsets(&self) -> Option<Series> {
401 None
402 }
403
404 fn propagate_nulls(&self) -> Option<Series> {
409 None
410 }
411
412 fn deposit(&self, validity: &Bitmap) -> Series;
413
414 fn find_validity_mismatch(&self, other: &Series, idxs: &mut Vec<IdxSize>);
416
417 fn cast(&self, _dtype: &DataType, options: CastOptions) -> PolarsResult<Series>;
418
419 fn get(&self, index: usize) -> PolarsResult<AnyValue<'_>> {
422 polars_ensure!(index < self.len(), oob = index, self.len());
423 let value = unsafe { self.get_unchecked(index) };
425 Ok(value)
426 }
427
428 unsafe fn get_unchecked(&self, _index: usize) -> AnyValue<'_>;
436
437 fn sort_with(&self, _options: SortOptions) -> PolarsResult<Series> {
438 polars_bail!(opq = sort_with, self._dtype());
439 }
440
441 #[allow(unused)]
443 fn arg_sort(&self, options: SortOptions) -> IdxCa {
444 invalid_operation_panic!(arg_sort, self)
445 }
446
447 fn null_count(&self) -> usize;
449
450 fn has_nulls(&self) -> bool;
452
453 fn unique(&self) -> PolarsResult<Series> {
455 polars_bail!(opq = unique, self._dtype());
456 }
457
458 fn n_unique(&self) -> PolarsResult<usize> {
462 polars_bail!(opq = n_unique, self._dtype());
463 }
464
465 fn arg_unique(&self) -> PolarsResult<IdxCa> {
467 polars_bail!(opq = arg_unique, self._dtype());
468 }
469
470 fn unique_id(&self) -> PolarsResult<(IdxSize, Vec<IdxSize>)> {
474 polars_bail!(opq = unique_id, self._dtype());
475 }
476
477 fn is_null(&self) -> BooleanChunked;
479
480 fn is_not_null(&self) -> BooleanChunked;
482
483 fn reverse(&self) -> Series;
485
486 fn as_single_ptr(&mut self) -> PolarsResult<usize> {
489 polars_bail!(opq = as_single_ptr, self._dtype());
490 }
491
492 fn shift(&self, _periods: i64) -> Series;
519
520 fn sum_reduce(&self) -> PolarsResult<Scalar> {
525 polars_bail!(opq = sum, self._dtype());
526 }
527 fn max_reduce(&self) -> PolarsResult<Scalar> {
529 polars_bail!(opq = max, self._dtype());
530 }
531 fn min_reduce(&self) -> PolarsResult<Scalar> {
533 polars_bail!(opq = min, self._dtype());
534 }
535 fn median_reduce(&self) -> PolarsResult<Scalar> {
537 polars_bail!(opq = median, self._dtype());
538 }
539 fn mean_reduce(&self) -> PolarsResult<Scalar> {
541 polars_bail!(opq = mean, self._dtype());
542 }
543 fn var_reduce(&self, _ddof: u8) -> PolarsResult<Scalar> {
545 polars_bail!(opq = var, self._dtype());
546 }
547 fn std_reduce(&self, _ddof: u8) -> PolarsResult<Scalar> {
549 polars_bail!(opq = std, self._dtype());
550 }
551 fn quantile_reduce(&self, _quantile: f64, _method: QuantileMethod) -> PolarsResult<Scalar> {
553 polars_bail!(opq = quantile, self._dtype());
554 }
555 fn quantiles_reduce(
557 &self,
558 _quantiles: &[f64],
559 _method: QuantileMethod,
560 ) -> PolarsResult<Scalar> {
561 polars_bail!(opq = quantiles, self._dtype());
562 }
563 fn and_reduce(&self) -> PolarsResult<Scalar> {
565 polars_bail!(opq = and_reduce, self._dtype());
566 }
567 fn or_reduce(&self) -> PolarsResult<Scalar> {
569 polars_bail!(opq = or_reduce, self._dtype());
570 }
571 fn xor_reduce(&self) -> PolarsResult<Scalar> {
573 polars_bail!(opq = xor_reduce, self._dtype());
574 }
575
576 fn first(&self) -> Scalar {
580 let dt = self.dtype();
581 let av = self.get(0).map_or(AnyValue::Null, AnyValue::into_static);
582
583 Scalar::new(dt.clone(), av)
584 }
585
586 fn first_non_null(&self) -> Scalar {
590 let av = if self.len() == 0 {
591 AnyValue::Null
592 } else {
593 let idx = if self.has_nulls() {
594 first_non_null(self.chunks().iter().map(|c| c.as_ref())).unwrap_or(0)
595 } else {
596 0
597 };
598 self.get(idx).map_or(AnyValue::Null, AnyValue::into_static)
599 };
600 Scalar::new(self.dtype().clone(), av)
601 }
602
603 fn last(&self) -> Scalar {
607 let dt = self.dtype();
608 let av = if self.len() == 0 {
609 AnyValue::Null
610 } else {
611 unsafe { self.get_unchecked(self.len() - 1) }.into_static()
613 };
614
615 Scalar::new(dt.clone(), av)
616 }
617
618 fn last_non_null(&self) -> Scalar {
622 let n = self.len();
623 let av = if n == 0 {
624 AnyValue::Null
625 } else {
626 let idx = if self.has_nulls() {
627 last_non_null(self.chunks().iter().map(|c| c.as_ref()), n).unwrap_or(n - 1)
628 } else {
629 n - 1
630 };
631 unsafe { self.get_unchecked(idx) }.into_static()
633 };
634 Scalar::new(self.dtype().clone(), av)
635 }
636
637 #[cfg(feature = "approx_unique")]
638 fn approx_n_unique(&self) -> PolarsResult<IdxSize> {
639 polars_bail!(opq = approx_n_unique, self._dtype());
640 }
641
642 fn clone_inner(&self) -> Arc<dyn SeriesTrait>;
644
645 #[cfg(feature = "object")]
646 fn get_object(&self, _index: usize) -> Option<&dyn PolarsObjectSafe> {
648 invalid_operation_panic!(get_object, self)
649 }
650
651 #[cfg(feature = "object")]
652 unsafe fn get_object_chunked_unchecked(
657 &self,
658 _chunk: usize,
659 _index: usize,
660 ) -> Option<&dyn PolarsObjectSafe> {
661 invalid_operation_panic!(get_object_chunked_unchecked, self)
662 }
663
664 fn as_any(&self) -> &dyn Any;
667
668 fn as_any_mut(&mut self) -> &mut dyn Any;
671
672 fn as_phys_any(&self) -> &dyn Any;
675
676 fn as_arc_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
677
678 #[cfg(feature = "checked_arithmetic")]
679 fn checked_div(&self, _rhs: &Series) -> PolarsResult<Series> {
680 polars_bail!(opq = checked_div, self._dtype());
681 }
682
683 #[cfg(feature = "rolling_window")]
684 fn rolling_map(
687 &self,
688 _f: &dyn Fn(&Series) -> PolarsResult<Series>,
689 _options: RollingOptionsFixedWindow,
690 ) -> PolarsResult<Series> {
691 polars_bail!(opq = rolling_map, self._dtype());
692 }
693}
694
695impl dyn SeriesTrait + '_ {
696 pub fn unpack<T: PolarsPhysicalType>(&self) -> PolarsResult<&ChunkedArray<T>> {
697 polars_ensure!(&T::get_static_dtype() == self.dtype(), unpack);
698 Ok(self.as_ref())
699 }
700}