Skip to main content

polars_core/series/
series_trait.rs

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        /// Return a bit representation
49        ///
50        /// If there is no available bit representation this returns `None`.
51        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        /// Get field (used in schema)
66        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        /// # Safety
93        ///
94        /// Does no bounds checks, groups must be correct.
95        #[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        /// # Safety
100        ///
101        /// Does no bounds checks, groups must be correct.
102        #[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        /// # Safety
107        ///
108        /// Does no bounds checks, groups must be correct.
109        #[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        /// # Safety
115        ///
116        /// Does no bounds checks, groups must be correct.
117        #[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        /// If the [`DataType`] is one of `{Int8, UInt8, Int16, UInt16}` the `Series` is
123        /// first cast to `Int64` to prevent overflow issues.
124        #[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        /// # Safety
129        ///
130        /// Does no bounds checks, groups must be correct.
131        #[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        /// # Safety
136        ///
137        /// Does no bounds checks, groups must be correct.
138        #[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        /// # Safety
143        ///
144        /// Does no bounds checks, groups must be correct.
145        #[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        /// # Safety
151        ///
152        /// Does no bounds checks, groups must be correct.
153        #[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        /// # Safety
159        ///
160        /// Does no bounds checks, groups must be correct.
161        #[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        /// # Safety
167        ///
168        /// Does no bounds checks, groups must be correct.
169        #[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    /// Rename the Series.
217    fn rename(&mut self, name: PlSmallStr);
218
219    /// Get the lengths of the underlying chunks
220    fn chunk_lengths(&self) -> ChunkLenIter<'_>;
221
222    /// Name of series.
223    fn name(&self) -> &PlSmallStr;
224
225    /// Get field (used in schema)
226    fn field(&self) -> Cow<'_, Field> {
227        self._field()
228    }
229
230    /// Get datatype of series.
231    fn dtype(&self) -> &DataType {
232        self._dtype()
233    }
234
235    /// Underlying chunks.
236    fn chunks(&self) -> &Vec<ArrayRef>;
237
238    /// Underlying chunks.
239    ///
240    /// # Safety
241    /// The caller must ensure the length and the data types of `ArrayRef` does not change.
242    unsafe fn chunks_mut(&mut self) -> &mut Vec<ArrayRef>;
243
244    /// Number of chunks in this Series
245    fn n_chunks(&self) -> usize {
246        self.chunks().len()
247    }
248
249    /// Shrink the capacity of this array to fit its length.
250    fn shrink_to_fit(&mut self) {
251        // no-op
252    }
253
254    /// Take `num_elements` from the top as a zero copy view.
255    fn limit(&self, num_elements: usize) -> Series {
256        self.slice(0, num_elements)
257    }
258
259    /// Get a zero copy view of the data.
260    ///
261    /// When offset is negative the offset is counted from the
262    /// end of the array
263    fn slice(&self, _offset: i64, _length: usize) -> Series;
264
265    /// Get a zero copy view of the data.
266    ///
267    /// When offset is negative the offset is counted from the
268    /// end of the array
269    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    /// Filter by boolean mask. This operation clones data.
278    fn filter(&self, _filter: &BooleanChunked) -> PolarsResult<Series>;
279
280    /// Take from `self` at the indexes given by `idx`.
281    ///
282    /// Null values in `idx` because null values in the output array.
283    ///
284    /// This operation is clone.
285    fn take(&self, _indices: &IdxCa) -> PolarsResult<Series>;
286
287    /// Take from `self` at the indexes given by `idx`.
288    ///
289    /// Null values in `idx` because null values in the output array.
290    ///
291    /// # Safety
292    /// This doesn't check any bounds.
293    unsafe fn take_unchecked(&self, _idx: &IdxCa) -> Series;
294
295    /// Take from `self` at the indexes given by `idx`.
296    ///
297    /// This operation is clone.
298    fn take_slice(&self, _indices: &[IdxSize]) -> PolarsResult<Series>;
299
300    /// Take from `self` at the indexes given by `idx`.
301    ///
302    /// # Safety
303    /// This doesn't check any bounds.
304    unsafe fn take_slice_unchecked(&self, _idx: &[IdxSize]) -> Series;
305
306    /// Get length of series.
307    fn len(&self) -> usize;
308
309    /// Check if Series is empty.
310    fn is_empty(&self) -> bool {
311        self.len() == 0
312    }
313
314    /// Check if Series only consists of nulls.
315    fn is_full_null(&self) -> bool {
316        self.len() == self.null_count()
317    }
318
319    /// Aggregate all chunks to a contiguous array of memory.
320    fn rechunk(&self) -> Series;
321
322    /// Returns the validity of this series as a single bitmap.
323    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    /// Sets the validity mask of this Series to the given bitmap.
344    fn with_validity(&self, validity: Option<Bitmap>) -> Series;
345
346    /// Drop all null values and return a new Series.
347    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    /// Returns the sum of the array as an f64.
356    fn _sum_as_f64(&self) -> f64 {
357        invalid_operation_panic!(_sum_as_f64, self)
358    }
359
360    /// Returns the mean value in the array
361    /// Returns an option because the array is nullable.
362    fn mean(&self) -> Option<f64> {
363        None
364    }
365
366    /// Returns the std value in the array
367    /// Returns an option because the array is nullable.
368    fn std(&self, _ddof: u8) -> Option<f64> {
369        None
370    }
371
372    /// Returns the var value in the array
373    /// Returns an option because the array is nullable.
374    fn var(&self, _ddof: u8) -> Option<f64> {
375        None
376    }
377
378    /// Returns the median value in the array
379    /// Returns an option because the array is nullable.
380    fn median(&self) -> Option<f64> {
381        None
382    }
383
384    /// Create a new Series filled with values from the given index.
385    ///
386    /// # Example
387    ///
388    /// ```rust
389    /// use polars_core::prelude::*;
390    /// let s = Series::new("a".into(), [0i32, 1, 8]);
391    /// let s2 = s.new_from_index(2, 4);
392    /// assert_eq!(Vec::from(s2.i32().unwrap()), &[Some(8), Some(8), Some(8), Some(8)])
393    /// ```
394    fn new_from_index(&self, _index: usize, _length: usize) -> Series;
395
396    /// Trim all lists of unused start and end elements recursively.
397    ///
398    /// - `None` if nothing needed to be done.
399    /// - `Some(series)` if something changed.
400    fn trim_lists_to_normalized_offsets(&self) -> Option<Series> {
401        None
402    }
403
404    /// Propagate down nulls in nested types.
405    ///
406    /// - `None` if nothing needed to be done.
407    /// - `Some(series)` if something changed.
408    fn propagate_nulls(&self) -> Option<Series> {
409        None
410    }
411
412    fn deposit(&self, validity: &Bitmap) -> Series;
413
414    /// Find the indices of elements where the null masks are different recursively.
415    fn find_validity_mismatch(&self, other: &Series, idxs: &mut Vec<IdxSize>);
416
417    fn cast(&self, _dtype: &DataType, options: CastOptions) -> PolarsResult<Series>;
418
419    /// Get a single value by index. Don't use this operation for loops as a runtime cast is
420    /// needed for every iteration.
421    fn get(&self, index: usize) -> PolarsResult<AnyValue<'_>> {
422        polars_ensure!(index < self.len(), oob = index, self.len());
423        // SAFETY: Just did bounds check
424        let value = unsafe { self.get_unchecked(index) };
425        Ok(value)
426    }
427
428    /// Get a single value by index. Don't use this operation for loops as a runtime cast is
429    /// needed for every iteration.
430    ///
431    /// This may refer to physical types
432    ///
433    /// # Safety
434    /// Does not do any bounds checking
435    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    /// Retrieve the indexes needed for a sort.
442    #[allow(unused)]
443    fn arg_sort(&self, options: SortOptions) -> IdxCa {
444        invalid_operation_panic!(arg_sort, self)
445    }
446
447    /// Count the null values.
448    fn null_count(&self) -> usize;
449
450    /// Return if any the chunks in this [`ChunkedArray`] have nulls.
451    fn has_nulls(&self) -> bool;
452
453    /// Get unique values in the Series.
454    fn unique(&self) -> PolarsResult<Series> {
455        polars_bail!(opq = unique, self._dtype());
456    }
457
458    /// Get unique values in the Series.
459    ///
460    /// A `null` value also counts as a unique value.
461    fn n_unique(&self) -> PolarsResult<usize> {
462        polars_bail!(opq = n_unique, self._dtype());
463    }
464
465    /// Get first indexes of unique values.
466    fn arg_unique(&self) -> PolarsResult<IdxCa> {
467        polars_bail!(opq = arg_unique, self._dtype());
468    }
469
470    /// Get dense ids for each unique value.
471    ///
472    /// Returns: (n_unique, unique_ids)
473    fn unique_id(&self) -> PolarsResult<(IdxSize, Vec<IdxSize>)>;
474
475    /// Get a mask of the null values.
476    fn is_null(&self) -> BooleanChunked;
477
478    /// Get a mask of the non-null values.
479    fn is_not_null(&self) -> BooleanChunked;
480
481    /// return a Series in reversed order
482    fn reverse(&self) -> Series;
483
484    /// Rechunk and return a pointer to the start of the Series.
485    /// Only implemented for numeric types
486    fn as_single_ptr(&mut self) -> PolarsResult<usize> {
487        polars_bail!(opq = as_single_ptr, self._dtype());
488    }
489
490    /// Shift the values by a given period and fill the parts that will be empty due to this operation
491    /// with `Nones`.
492    ///
493    /// *NOTE: If you want to fill the Nones with a value use the
494    /// [`shift` operation on `ChunkedArray<T>`](../chunked_array/ops/trait.ChunkShift.html).*
495    ///
496    /// # Example
497    ///
498    /// ```rust
499    /// # use polars_core::prelude::*;
500    /// fn example() -> PolarsResult<()> {
501    ///     let s = Series::new("series".into(), &[1, 2, 3]);
502    ///
503    ///     let shifted = s.shift(1);
504    ///     assert_eq!(Vec::from(shifted.i32()?), &[None, Some(1), Some(2)]);
505    ///
506    ///     let shifted = s.shift(-1);
507    ///     assert_eq!(Vec::from(shifted.i32()?), &[Some(2), Some(3), None]);
508    ///
509    ///     let shifted = s.shift(2);
510    ///     assert_eq!(Vec::from(shifted.i32()?), &[None, None, Some(1)]);
511    ///
512    ///     Ok(())
513    /// }
514    /// example();
515    /// ```
516    fn shift(&self, _periods: i64) -> Series;
517
518    /// Get the sum of the Series as a new Scalar.
519    ///
520    /// If the [`DataType`] is one of `{Int8, UInt8, Int16, UInt16}` the `Series` is
521    /// first cast to `Int64` to prevent overflow issues.
522    fn sum_reduce(&self) -> PolarsResult<Scalar> {
523        polars_bail!(opq = sum, self._dtype());
524    }
525    /// Get the max of the Series as a new Series of length 1.
526    fn max_reduce(&self) -> PolarsResult<Scalar> {
527        polars_bail!(opq = max, self._dtype());
528    }
529    /// Get the min of the Series as a new Series of length 1.
530    fn min_reduce(&self) -> PolarsResult<Scalar> {
531        polars_bail!(opq = min, self._dtype());
532    }
533    /// Get the median of the Series as a new Series of length 1.
534    fn median_reduce(&self) -> PolarsResult<Scalar> {
535        polars_bail!(opq = median, self._dtype());
536    }
537    /// Get the mean of the Series as a new Scalar
538    fn mean_reduce(&self) -> PolarsResult<Scalar> {
539        polars_bail!(opq = mean, self._dtype());
540    }
541    /// Get the variance of the Series as a new Series of length 1.
542    fn var_reduce(&self, _ddof: u8) -> PolarsResult<Scalar> {
543        polars_bail!(opq = var, self._dtype());
544    }
545    /// Get the standard deviation of the Series as a new Series of length 1.
546    fn std_reduce(&self, _ddof: u8) -> PolarsResult<Scalar> {
547        polars_bail!(opq = std, self._dtype());
548    }
549    /// Get the quantile of the Series as a new Series of length 1.
550    fn quantile_reduce(&self, _quantile: f64, _method: QuantileMethod) -> PolarsResult<Scalar> {
551        polars_bail!(opq = quantile, self._dtype());
552    }
553    /// Get multiple quantiles of the ChunkedArray as a new `List` Scalar
554    fn quantiles_reduce(
555        &self,
556        _quantiles: &[f64],
557        _method: QuantileMethod,
558    ) -> PolarsResult<Scalar> {
559        polars_bail!(opq = quantiles, self._dtype());
560    }
561    /// Get the bitwise AND of the Series as a new Series of length 1,
562    fn and_reduce(&self) -> PolarsResult<Scalar> {
563        polars_bail!(opq = and_reduce, self._dtype());
564    }
565    /// Get the bitwise OR of the Series as a new Series of length 1,
566    fn or_reduce(&self) -> PolarsResult<Scalar> {
567        polars_bail!(opq = or_reduce, self._dtype());
568    }
569    /// Get the bitwise XOR of the Series as a new Series of length 1,
570    fn xor_reduce(&self) -> PolarsResult<Scalar> {
571        polars_bail!(opq = xor_reduce, self._dtype());
572    }
573
574    /// Get the first element of the [`Series`] as a [`Scalar`]
575    ///
576    /// If the [`Series`] is empty, a [`Scalar`] with a [`AnyValue::Null`] is returned.
577    fn first(&self) -> Scalar {
578        let dt = self.dtype();
579        let av = self.get(0).map_or(AnyValue::Null, AnyValue::into_static);
580
581        Scalar::new(dt.clone(), av)
582    }
583
584    /// Get the first non-null element of the [`Series`] as a [`Scalar`]
585    ///
586    /// If the [`Series`] is empty, a [`Scalar`] with a [`AnyValue::Null`] is returned.
587    fn first_non_null(&self) -> Scalar {
588        let av = if self.len() == 0 {
589            AnyValue::Null
590        } else {
591            let idx = if self.has_nulls() {
592                first_non_null(self.chunks().iter().map(|c| c.as_ref())).unwrap_or(0)
593            } else {
594                0
595            };
596            self.get(idx).map_or(AnyValue::Null, AnyValue::into_static)
597        };
598        Scalar::new(self.dtype().clone(), av)
599    }
600
601    /// Get the last element of the [`Series`] as a [`Scalar`]
602    ///
603    /// If the [`Series`] is empty, a [`Scalar`] with a [`AnyValue::Null`] is returned.
604    fn last(&self) -> Scalar {
605        let dt = self.dtype();
606        let av = if self.len() == 0 {
607            AnyValue::Null
608        } else {
609            // SAFETY: len-1 < len if len != 0
610            unsafe { self.get_unchecked(self.len() - 1) }.into_static()
611        };
612
613        Scalar::new(dt.clone(), av)
614    }
615
616    /// Get the last non-null element of the [`Series`] as a [`Scalar`]
617    ///
618    /// If the [`Series`] is empty, a [`Scalar`] with a [`AnyValue::Null`] is returned.
619    fn last_non_null(&self) -> Scalar {
620        let n = self.len();
621        let av = if n == 0 {
622            AnyValue::Null
623        } else {
624            let idx = if self.has_nulls() {
625                last_non_null(self.chunks().iter().map(|c| c.as_ref()), n).unwrap_or(n - 1)
626            } else {
627                n - 1
628            };
629            // SAFETY: len-1 < len if len != 0
630            unsafe { self.get_unchecked(idx) }.into_static()
631        };
632        Scalar::new(self.dtype().clone(), av)
633    }
634
635    #[cfg(feature = "approx_unique")]
636    fn approx_n_unique(&self) -> PolarsResult<IdxSize> {
637        polars_bail!(opq = approx_n_unique, self._dtype());
638    }
639
640    /// Clone inner ChunkedArray and wrap in a new Arc
641    fn clone_inner(&self) -> Arc<dyn SeriesTrait>;
642
643    #[cfg(feature = "object")]
644    /// Get the value at this index as a downcastable Any trait ref.
645    fn get_object(&self, _index: usize) -> Option<&dyn PolarsObjectSafe> {
646        invalid_operation_panic!(get_object, self)
647    }
648
649    #[cfg(feature = "object")]
650    /// Get the value at this index as a downcastable Any trait ref.
651    ///
652    /// # Safety
653    /// This function doesn't do any bound checks.
654    unsafe fn get_object_chunked_unchecked(
655        &self,
656        _chunk: usize,
657        _index: usize,
658    ) -> Option<&dyn PolarsObjectSafe> {
659        invalid_operation_panic!(get_object_chunked_unchecked, self)
660    }
661
662    /// Get a hold of the [`ChunkedArray`], [`Logical`] or `NullChunked` as an `Any` trait
663    /// reference.
664    fn as_any(&self) -> &dyn Any;
665
666    /// Get a hold of the [`ChunkedArray`], [`Logical`] or `NullChunked` as an `Any` trait mutable
667    /// reference.
668    fn as_any_mut(&mut self) -> &mut dyn Any;
669
670    /// Get a hold of the [`ChunkedArray`] or `NullChunked` as an `Any` trait reference. This
671    /// pierces through `Logical` types to get the underlying physical array.
672    fn as_phys_any(&self) -> &dyn Any;
673
674    fn as_arc_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
675
676    #[cfg(feature = "checked_arithmetic")]
677    fn checked_div(&self, _rhs: &Series) -> PolarsResult<Series> {
678        polars_bail!(opq = checked_div, self._dtype());
679    }
680
681    #[cfg(feature = "rolling_window")]
682    /// Apply a custom function over a rolling/ moving window of the array.
683    /// This has quite some dynamic dispatch, so prefer rolling_min, max, mean, sum over this.
684    fn rolling_map(
685        &self,
686        _f: &dyn Fn(&Series) -> PolarsResult<Series>,
687        _options: RollingOptionsFixedWindow,
688    ) -> PolarsResult<Series> {
689        polars_bail!(opq = rolling_map, self._dtype());
690    }
691}
692
693impl dyn SeriesTrait + '_ {
694    pub fn unpack<T: PolarsPhysicalType>(&self) -> PolarsResult<&ChunkedArray<T>> {
695        polars_ensure!(&T::get_static_dtype() == self.dtype(), unpack);
696        Ok(self.as_ref())
697    }
698}