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