1use std::any::Any;
2use std::borrow::Cow;
3
4use arrow::bitmap::{Bitmap, BitmapBuilder};
5use polars_compute::rolling::QuantileMethod;
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9use crate::chunked_array::cast::CastOptions;
10#[cfg(feature = "object")]
11use crate::chunked_array::object::PolarsObjectSafe;
12use crate::prelude::*;
13
14#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16#[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]
17pub enum IsSorted {
18 Ascending,
19 Descending,
20 Not,
21}
22
23impl IsSorted {
24 pub fn reverse(self) -> Self {
25 use IsSorted::*;
26 match self {
27 Ascending => Descending,
28 Descending => Ascending,
29 Not => Not,
30 }
31 }
32}
33
34pub enum BitRepr {
35 U8(UInt8Chunked),
36 U16(UInt16Chunked),
37 U32(UInt32Chunked),
38 U64(UInt64Chunked),
39 #[cfg(feature = "dtype-u128")]
40 U128(UInt128Chunked),
41}
42
43pub(crate) mod private {
44 use polars_utils::aliases::PlSeedableRandomStateQuality;
45
46 use super::*;
47 use crate::chunked_array::flags::StatisticsFlags;
48 use crate::chunked_array::ops::compare_inner::{TotalEqInner, TotalOrdInner};
49
50 pub trait PrivateSeriesNumeric {
51 fn bit_repr(&self) -> Option<BitRepr>;
55 }
56
57 pub trait PrivateSeries {
58 #[cfg(feature = "object")]
59 fn get_list_builder(
60 &self,
61 _name: PlSmallStr,
62 _values_capacity: usize,
63 _list_capacity: usize,
64 ) -> Box<dyn ListBuilderTrait> {
65 invalid_operation_panic!(get_list_builder, self)
66 }
67
68 fn _field(&self) -> Cow<'_, Field>;
70
71 fn _dtype(&self) -> &DataType;
72
73 fn compute_len(&mut self);
74
75 fn _get_flags(&self) -> StatisticsFlags;
76
77 fn _set_flags(&mut self, flags: StatisticsFlags);
78
79 unsafe fn equal_element(
80 &self,
81 _idx_self: usize,
82 _idx_other: usize,
83 _other: &Series,
84 ) -> bool {
85 invalid_operation_panic!(equal_element, self)
86 }
87 #[expect(clippy::wrong_self_convention)]
88 fn into_total_eq_inner<'a>(&'a self) -> Box<dyn TotalEqInner + 'a>;
89 #[expect(clippy::wrong_self_convention)]
90 fn into_total_ord_inner<'a>(&'a self) -> Box<dyn TotalOrdInner + 'a>;
91
92 fn vec_hash(
93 &self,
94 _build_hasher: PlSeedableRandomStateQuality,
95 _buf: &mut Vec<u64>,
96 ) -> PolarsResult<()>;
97 fn vec_hash_combine(
98 &self,
99 _build_hasher: PlSeedableRandomStateQuality,
100 _hashes: &mut [u64],
101 ) -> PolarsResult<()>;
102
103 #[cfg(feature = "algorithm_group_by")]
107 unsafe fn agg_min(&self, groups: &GroupsType) -> Series {
108 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
109 }
110 #[cfg(feature = "algorithm_group_by")]
114 unsafe fn agg_max(&self, groups: &GroupsType) -> Series {
115 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
116 }
117 #[cfg(feature = "algorithm_group_by")]
120 unsafe fn agg_sum(&self, groups: &GroupsType) -> Series {
121 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
122 }
123 #[cfg(feature = "algorithm_group_by")]
127 unsafe fn agg_std(&self, groups: &GroupsType, _ddof: u8) -> Series {
128 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
129 }
130 #[cfg(feature = "algorithm_group_by")]
134 unsafe fn agg_var(&self, groups: &GroupsType, _ddof: u8) -> Series {
135 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
136 }
137 #[cfg(feature = "algorithm_group_by")]
141 unsafe fn agg_list(&self, groups: &GroupsType) -> Series {
142 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
143 }
144
145 #[cfg(feature = "bitwise")]
149 unsafe fn agg_and(&self, groups: &GroupsType) -> Series {
150 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
151 }
152
153 #[cfg(feature = "bitwise")]
157 unsafe fn agg_or(&self, groups: &GroupsType) -> Series {
158 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
159 }
160
161 #[cfg(feature = "bitwise")]
165 unsafe fn agg_xor(&self, groups: &GroupsType) -> Series {
166 Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
167 }
168
169 fn subtract(&self, _rhs: &Series) -> PolarsResult<Series> {
170 polars_bail!(opq = subtract, self._dtype());
171 }
172 fn add_to(&self, _rhs: &Series) -> PolarsResult<Series> {
173 polars_bail!(opq = add, self._dtype());
174 }
175 fn multiply(&self, _rhs: &Series) -> PolarsResult<Series> {
176 polars_bail!(opq = multiply, self._dtype());
177 }
178 fn divide(&self, _rhs: &Series) -> PolarsResult<Series> {
179 polars_bail!(opq = divide, self._dtype());
180 }
181 fn remainder(&self, _rhs: &Series) -> PolarsResult<Series> {
182 polars_bail!(opq = remainder, self._dtype());
183 }
184 #[cfg(feature = "algorithm_group_by")]
185 fn group_tuples(&self, _multithreaded: bool, _sorted: bool) -> PolarsResult<GroupsType> {
186 polars_bail!(opq = group_tuples, self._dtype());
187 }
188 #[cfg(feature = "zip_with")]
189 fn zip_with_same_type(
190 &self,
191 _mask: &BooleanChunked,
192 _other: &Series,
193 ) -> PolarsResult<Series> {
194 polars_bail!(opq = zip_with_same_type, self._dtype());
195 }
196
197 #[allow(unused_variables)]
198 fn arg_sort_multiple(
199 &self,
200 by: &[Column],
201 _options: &SortMultipleOptions,
202 ) -> PolarsResult<IdxCa> {
203 polars_bail!(opq = arg_sort_multiple, self._dtype());
204 }
205 }
206}
207
208pub trait SeriesTrait:
209 Send + Sync + private::PrivateSeries + private::PrivateSeriesNumeric
210{
211 fn rename(&mut self, name: PlSmallStr);
213
214 fn chunk_lengths(&self) -> ChunkLenIter<'_>;
216
217 fn name(&self) -> &PlSmallStr;
219
220 fn field(&self) -> Cow<'_, Field> {
222 self._field()
223 }
224
225 fn dtype(&self) -> &DataType {
227 self._dtype()
228 }
229
230 fn chunks(&self) -> &Vec<ArrayRef>;
232
233 unsafe fn chunks_mut(&mut self) -> &mut Vec<ArrayRef>;
238
239 fn n_chunks(&self) -> usize {
241 self.chunks().len()
242 }
243
244 fn shrink_to_fit(&mut self) {
246 }
248
249 fn limit(&self, num_elements: usize) -> Series {
251 self.slice(0, num_elements)
252 }
253
254 fn slice(&self, _offset: i64, _length: usize) -> Series;
259
260 fn split_at(&self, _offset: i64) -> (Series, Series);
265
266 fn append(&mut self, other: &Series) -> PolarsResult<()>;
267 fn append_owned(&mut self, other: Series) -> PolarsResult<()>;
268
269 #[doc(hidden)]
270 fn extend(&mut self, _other: &Series) -> PolarsResult<()>;
271
272 fn filter(&self, _filter: &BooleanChunked) -> PolarsResult<Series>;
274
275 fn take(&self, _indices: &IdxCa) -> PolarsResult<Series>;
281
282 unsafe fn take_unchecked(&self, _idx: &IdxCa) -> Series;
289
290 fn take_slice(&self, _indices: &[IdxSize]) -> PolarsResult<Series>;
294
295 unsafe fn take_slice_unchecked(&self, _idx: &[IdxSize]) -> Series;
300
301 fn len(&self) -> usize;
303
304 fn is_empty(&self) -> bool {
306 self.len() == 0
307 }
308
309 fn rechunk(&self) -> Series;
311
312 fn rechunk_validity(&self) -> Option<Bitmap> {
313 if self.chunks().len() == 1 {
314 return self.chunks()[0].validity().cloned();
315 }
316
317 if !self.has_nulls() || self.is_empty() {
318 return None;
319 }
320
321 let mut bm = BitmapBuilder::with_capacity(self.len());
322 for arr in self.chunks() {
323 if let Some(v) = arr.validity() {
324 bm.extend_from_bitmap(v);
325 } else {
326 bm.extend_constant(arr.len(), true);
327 }
328 }
329 bm.into_opt_validity()
330 }
331
332 fn drop_nulls(&self) -> Series {
334 if self.null_count() == 0 {
335 Series(self.clone_inner())
336 } else {
337 self.filter(&self.is_not_null()).unwrap()
338 }
339 }
340
341 fn _sum_as_f64(&self) -> f64 {
343 invalid_operation_panic!(_sum_as_f64, self)
344 }
345
346 fn mean(&self) -> Option<f64> {
349 None
350 }
351
352 fn std(&self, _ddof: u8) -> Option<f64> {
355 None
356 }
357
358 fn var(&self, _ddof: u8) -> Option<f64> {
361 None
362 }
363
364 fn median(&self) -> Option<f64> {
367 None
368 }
369
370 fn new_from_index(&self, _index: usize, _length: usize) -> Series;
381
382 fn trim_lists_to_normalized_offsets(&self) -> Option<Series> {
387 None
388 }
389
390 fn propagate_nulls(&self) -> Option<Series> {
395 None
396 }
397
398 fn find_validity_mismatch(&self, other: &Series, idxs: &mut Vec<IdxSize>);
400
401 fn cast(&self, _dtype: &DataType, options: CastOptions) -> PolarsResult<Series>;
402
403 fn get(&self, index: usize) -> PolarsResult<AnyValue<'_>> {
406 polars_ensure!(index < self.len(), oob = index, self.len());
407 let value = unsafe { self.get_unchecked(index) };
409 Ok(value)
410 }
411
412 unsafe fn get_unchecked(&self, _index: usize) -> AnyValue<'_>;
420
421 fn sort_with(&self, _options: SortOptions) -> PolarsResult<Series> {
422 polars_bail!(opq = sort_with, self._dtype());
423 }
424
425 #[allow(unused)]
427 fn arg_sort(&self, options: SortOptions) -> IdxCa {
428 invalid_operation_panic!(arg_sort, self)
429 }
430
431 fn null_count(&self) -> usize;
433
434 fn has_nulls(&self) -> bool;
436
437 fn unique(&self) -> PolarsResult<Series> {
439 polars_bail!(opq = unique, self._dtype());
440 }
441
442 fn n_unique(&self) -> PolarsResult<usize> {
446 polars_bail!(opq = n_unique, self._dtype());
447 }
448
449 fn arg_unique(&self) -> PolarsResult<IdxCa> {
451 polars_bail!(opq = arg_unique, self._dtype());
452 }
453
454 fn is_null(&self) -> BooleanChunked;
456
457 fn is_not_null(&self) -> BooleanChunked;
459
460 fn reverse(&self) -> Series;
462
463 fn as_single_ptr(&mut self) -> PolarsResult<usize> {
466 polars_bail!(opq = as_single_ptr, self._dtype());
467 }
468
469 fn shift(&self, _periods: i64) -> Series;
496
497 fn sum_reduce(&self) -> PolarsResult<Scalar> {
502 polars_bail!(opq = sum, self._dtype());
503 }
504 fn max_reduce(&self) -> PolarsResult<Scalar> {
506 polars_bail!(opq = max, self._dtype());
507 }
508 fn min_reduce(&self) -> PolarsResult<Scalar> {
510 polars_bail!(opq = min, self._dtype());
511 }
512 fn median_reduce(&self) -> PolarsResult<Scalar> {
514 polars_bail!(opq = median, self._dtype());
515 }
516 fn var_reduce(&self, _ddof: u8) -> PolarsResult<Scalar> {
518 polars_bail!(opq = var, self._dtype());
519 }
520 fn std_reduce(&self, _ddof: u8) -> PolarsResult<Scalar> {
522 polars_bail!(opq = std, self._dtype());
523 }
524 fn quantile_reduce(&self, _quantile: f64, _method: QuantileMethod) -> PolarsResult<Scalar> {
526 polars_bail!(opq = quantile, self._dtype());
527 }
528 fn and_reduce(&self) -> PolarsResult<Scalar> {
530 polars_bail!(opq = and_reduce, self._dtype());
531 }
532 fn or_reduce(&self) -> PolarsResult<Scalar> {
534 polars_bail!(opq = or_reduce, self._dtype());
535 }
536 fn xor_reduce(&self) -> PolarsResult<Scalar> {
538 polars_bail!(opq = xor_reduce, self._dtype());
539 }
540
541 fn first(&self) -> Scalar {
545 let dt = self.dtype();
546 let av = self.get(0).map_or(AnyValue::Null, AnyValue::into_static);
547
548 Scalar::new(dt.clone(), av)
549 }
550
551 fn last(&self) -> Scalar {
555 let dt = self.dtype();
556 let av = if self.len() == 0 {
557 AnyValue::Null
558 } else {
559 unsafe { self.get_unchecked(self.len() - 1) }.into_static()
561 };
562
563 Scalar::new(dt.clone(), av)
564 }
565
566 #[cfg(feature = "approx_unique")]
567 fn approx_n_unique(&self) -> PolarsResult<IdxSize> {
568 polars_bail!(opq = approx_n_unique, self._dtype());
569 }
570
571 fn clone_inner(&self) -> Arc<dyn SeriesTrait>;
573
574 #[cfg(feature = "object")]
575 fn get_object(&self, _index: usize) -> Option<&dyn PolarsObjectSafe> {
577 invalid_operation_panic!(get_object, self)
578 }
579
580 #[cfg(feature = "object")]
581 unsafe fn get_object_chunked_unchecked(
586 &self,
587 _chunk: usize,
588 _index: usize,
589 ) -> Option<&dyn PolarsObjectSafe> {
590 invalid_operation_panic!(get_object_chunked_unchecked, self)
591 }
592
593 fn as_any(&self) -> &dyn Any;
596
597 fn as_any_mut(&mut self) -> &mut dyn Any;
600
601 fn as_phys_any(&self) -> &dyn Any;
604
605 fn as_arc_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
606
607 #[cfg(feature = "checked_arithmetic")]
608 fn checked_div(&self, _rhs: &Series) -> PolarsResult<Series> {
609 polars_bail!(opq = checked_div, self._dtype());
610 }
611
612 #[cfg(feature = "rolling_window")]
613 fn rolling_map(
616 &self,
617 _f: &dyn Fn(&Series) -> PolarsResult<Series>,
618 _options: RollingOptionsFixedWindow,
619 ) -> PolarsResult<Series> {
620 polars_bail!(opq = rolling_map, self._dtype());
621 }
622}
623
624impl dyn SeriesTrait + '_ {
625 pub fn unpack<T: PolarsPhysicalType>(&self) -> PolarsResult<&ChunkedArray<T>> {
626 polars_ensure!(&T::get_static_dtype() == self.dtype(), unpack);
627 Ok(self.as_ref())
628 }
629}