Skip to main content

polars_core/series/
series_trait.rs

1use std::any::Any;
2use std::borrow::Cow;
3
4use polars_arrow::bitmap::{Bitmap, BitmapBuilder};
5use polars_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::TotalOrdInner;
47
48    pub trait PrivateSeriesNumeric {
49        /// Return a bit representation
50        ///
51        /// If there is no available bit representation this returns `None`.
52        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        /// Get field (used in schema)
67        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_ord_inner<'a>(&'a self) -> Box<dyn TotalOrdInner + 'a>;
79
80        fn vec_hash(
81            &self,
82            _build_hasher: PlSeedableRandomStateQuality,
83            _buf: &mut Vec<u64>,
84        ) -> PolarsResult<()>;
85        fn vec_hash_combine(
86            &self,
87            _build_hasher: PlSeedableRandomStateQuality,
88            _hashes: &mut [u64],
89        ) -> PolarsResult<()>;
90
91        /// # Safety
92        ///
93        /// Does no bounds checks, groups must be correct.
94        #[cfg(feature = "algorithm_group_by")]
95        unsafe fn agg_min(&self, groups: &GroupsType) -> Series {
96            Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
97        }
98        /// # Safety
99        ///
100        /// Does no bounds checks, groups must be correct.
101        #[cfg(feature = "algorithm_group_by")]
102        unsafe fn agg_max(&self, groups: &GroupsType) -> Series {
103            Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
104        }
105        /// # Safety
106        ///
107        /// Does no bounds checks, groups must be correct.
108        #[cfg(feature = "algorithm_group_by")]
109        unsafe fn agg_arg_min(&self, groups: &GroupsType) -> Series {
110            Series::full_null(self._field().name().clone(), groups.len(), &IDX_DTYPE)
111        }
112
113        /// # Safety
114        ///
115        /// Does no bounds checks, groups must be correct.
116        #[cfg(feature = "algorithm_group_by")]
117        unsafe fn agg_arg_max(&self, groups: &GroupsType) -> Series {
118            Series::full_null(self._field().name().clone(), groups.len(), &IDX_DTYPE)
119        }
120
121        /// If the [`DataType`] is one of `{Int8, UInt8, Int16, UInt16}` the `Series` is
122        /// first cast to `Int64` to prevent overflow issues.
123        #[cfg(feature = "algorithm_group_by")]
124        unsafe fn agg_sum(&self, groups: &GroupsType) -> Series {
125            Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
126        }
127        /// # Safety
128        ///
129        /// Does no bounds checks, groups must be correct.
130        #[cfg(feature = "algorithm_group_by")]
131        unsafe fn agg_std(&self, groups: &GroupsType, _ddof: u8) -> Series {
132            Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
133        }
134        /// # Safety
135        ///
136        /// Does no bounds checks, groups must be correct.
137        #[cfg(feature = "algorithm_group_by")]
138        unsafe fn agg_var(&self, groups: &GroupsType, _ddof: u8) -> Series {
139            Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
140        }
141        /// # Safety
142        ///
143        /// Does no bounds checks, groups must be correct.
144        #[cfg(feature = "algorithm_group_by")]
145        unsafe fn agg_list(&self, groups: &GroupsType) -> Series {
146            Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
147        }
148
149        /// # Safety
150        ///
151        /// Does no bounds checks, groups must be correct.
152        #[cfg(feature = "bitwise")]
153        unsafe fn agg_and(&self, groups: &GroupsType) -> Series {
154            Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
155        }
156
157        /// # Safety
158        ///
159        /// Does no bounds checks, groups must be correct.
160        #[cfg(feature = "bitwise")]
161        unsafe fn agg_or(&self, groups: &GroupsType) -> Series {
162            Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
163        }
164
165        /// # Safety
166        ///
167        /// Does no bounds checks, groups must be correct.
168        #[cfg(feature = "bitwise")]
169        unsafe fn agg_xor(&self, groups: &GroupsType) -> Series {
170            Series::full_null(self._field().name().clone(), groups.len(), self._dtype())
171        }
172
173        fn subtract(&self, _rhs: &Series) -> PolarsResult<Series> {
174            polars_bail!(opq = subtract, self._dtype());
175        }
176        fn add_to(&self, _rhs: &Series) -> PolarsResult<Series> {
177            polars_bail!(opq = add, self._dtype());
178        }
179        fn multiply(&self, _rhs: &Series) -> PolarsResult<Series> {
180            polars_bail!(opq = multiply, self._dtype());
181        }
182        fn divide(&self, _rhs: &Series) -> PolarsResult<Series> {
183            polars_bail!(opq = divide, self._dtype());
184        }
185        fn remainder(&self, _rhs: &Series) -> PolarsResult<Series> {
186            polars_bail!(opq = remainder, self._dtype());
187        }
188        #[cfg(feature = "algorithm_group_by")]
189        fn group_tuples(&self, _multithreaded: bool, _sorted: bool) -> PolarsResult<GroupsType> {
190            polars_bail!(opq = group_tuples, self._dtype());
191        }
192        #[cfg(feature = "zip_with")]
193        fn zip_with_same_type(
194            &self,
195            _mask: &BooleanChunked,
196            _other: &Series,
197        ) -> PolarsResult<Series> {
198            polars_bail!(opq = zip_with_same_type, self._dtype());
199        }
200
201        #[allow(unused_variables)]
202        fn arg_sort_multiple(
203            &self,
204            by: &[Column],
205            _options: &SortMultipleOptions,
206        ) -> PolarsResult<IdxCa> {
207            polars_bail!(opq = arg_sort_multiple, self._dtype());
208        }
209    }
210}
211
212pub trait SeriesTrait:
213    Send + Sync + private::PrivateSeries + private::PrivateSeriesNumeric
214{
215    /// Rename the Series.
216    fn rename(&mut self, name: PlSmallStr);
217
218    /// Get the lengths of the underlying chunks
219    fn chunk_lengths(&self) -> ChunkLenIter<'_>;
220
221    /// Name of series.
222    fn name(&self) -> &PlSmallStr;
223
224    /// Get field (used in schema)
225    fn field(&self) -> Cow<'_, Field> {
226        self._field()
227    }
228
229    /// Get datatype of series.
230    #[inline(always)]
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    /// Applies the given mask to this Series, returning a new Series. If a
347    /// validity bit is true nothing changes, if it is false the corresponding
348    /// element becomes null.
349    fn mask(&self, validity: &Bitmap) -> Series {
350        if validity.len() == 1 {
351            if validity.get_bit(0) {
352                Series(self.clone_inner())
353            } else {
354                Series::full_null(self._field().name().clone(), self.len(), self._dtype())
355            }
356        } else if self.len() == 1 && validity.len() != 1 {
357            self.new_from_index(0, validity.len()).mask(validity)
358        } else {
359            self.with_validity(combine_validities_and(
360                self.rechunk_validity().as_ref(),
361                Some(validity),
362            ))
363        }
364    }
365
366    /// Drop all null values and return a new Series.
367    fn drop_nulls(&self) -> Series {
368        if self.null_count() == 0 {
369            Series(self.clone_inner())
370        } else {
371            self.filter(&self.is_not_null()).unwrap()
372        }
373    }
374
375    /// Returns the sum of the array as an f64.
376    fn _sum_as_f64(&self) -> f64 {
377        invalid_operation_panic!(_sum_as_f64, self)
378    }
379
380    /// Returns the mean value in the array
381    /// Returns an option because the array is nullable.
382    fn mean(&self) -> Option<f64> {
383        None
384    }
385
386    /// Returns the std value in the array
387    /// Returns an option because the array is nullable.
388    fn std(&self, _ddof: u8) -> Option<f64> {
389        None
390    }
391
392    /// Returns the var value in the array
393    /// Returns an option because the array is nullable.
394    fn var(&self, _ddof: u8) -> Option<f64> {
395        None
396    }
397
398    /// Returns the median value in the array
399    /// Returns an option because the array is nullable.
400    fn median(&self) -> Option<f64> {
401        None
402    }
403
404    /// Create a new Series filled with values from the given index.
405    ///
406    /// # Example
407    ///
408    /// ```rust
409    /// use polars_core::prelude::*;
410    /// let s = Series::new("a".into(), [0i32, 1, 8]);
411    /// let s2 = s.new_from_index(2, 4);
412    /// assert_eq!(Vec::from(s2.i32().unwrap()), &[Some(8), Some(8), Some(8), Some(8)])
413    /// ```
414    fn new_from_index(&self, _index: usize, _length: usize) -> Series;
415
416    /// Trim all lists of unused start and end elements recursively.
417    ///
418    /// - `None` if nothing needed to be done.
419    /// - `Some(series)` if something changed.
420    fn trim_lists_to_normalized_offsets(&self) -> Option<Series> {
421        None
422    }
423
424    /// Propagate down nulls in nested types.
425    ///
426    /// - `None` if nothing needed to be done.
427    /// - `Some(series)` if something changed.
428    fn propagate_nulls(&self) -> Option<Series> {
429        None
430    }
431
432    fn deposit(&self, validity: &Bitmap) -> Series;
433
434    /// Find the indices of elements where the null masks are different recursively.
435    fn find_validity_mismatch(&self, other: &Series, idxs: &mut Vec<IdxSize>);
436
437    fn cast(&self, _dtype: &DataType, options: CastOptions) -> PolarsResult<Series>;
438
439    /// Get a single value by index. Don't use this operation for loops as a runtime cast is
440    /// needed for every iteration.
441    fn get(&self, index: usize) -> PolarsResult<AnyValue<'_>> {
442        polars_ensure!(index < self.len(), oob = index, self.len());
443        // SAFETY: Just did bounds check
444        let value = unsafe { self.get_unchecked(index) };
445        Ok(value)
446    }
447
448    /// Get a single value by index. Don't use this operation for loops as a runtime cast is
449    /// needed for every iteration.
450    ///
451    /// This may refer to physical types
452    ///
453    /// # Safety
454    /// Does not do any bounds checking
455    unsafe fn get_unchecked(&self, _index: usize) -> AnyValue<'_>;
456
457    fn sort_with(&self, _options: SortOptions) -> PolarsResult<Series> {
458        polars_bail!(opq = sort_with, self._dtype());
459    }
460
461    /// Retrieve the indexes needed for a sort.
462    #[allow(unused)]
463    fn arg_sort(&self, options: SortOptions) -> IdxCa {
464        invalid_operation_panic!(arg_sort, self)
465    }
466
467    /// Count the null values.
468    fn null_count(&self) -> usize;
469
470    /// Return if any the chunks in this [`ChunkedArray`] have nulls.
471    fn has_nulls(&self) -> bool;
472
473    /// Get unique values in the Series.
474    fn unique(&self) -> PolarsResult<Series> {
475        polars_bail!(opq = unique, self._dtype());
476    }
477
478    /// Get unique values in the Series.
479    ///
480    /// A `null` value also counts as a unique value.
481    fn n_unique(&self) -> PolarsResult<usize> {
482        polars_bail!(opq = n_unique, self._dtype());
483    }
484
485    /// Get first indexes of unique values.
486    fn arg_unique(&self) -> PolarsResult<IdxCa> {
487        polars_bail!(opq = arg_unique, self._dtype());
488    }
489
490    /// Get dense ids for each unique value.
491    ///
492    /// Returns: (n_unique, unique_ids)
493    fn unique_id(&self) -> PolarsResult<(IdxSize, Vec<IdxSize>)> {
494        polars_bail!(opq = unique_id, self._dtype());
495    }
496
497    /// Get a mask of the null values.
498    fn is_null(&self) -> BooleanChunked;
499
500    /// Get a mask of the non-null values.
501    fn is_not_null(&self) -> BooleanChunked;
502
503    /// return a Series in reversed order
504    fn reverse(&self) -> Series;
505
506    /// Rechunk and return a pointer to the start of the Series.
507    /// Only implemented for numeric types
508    fn as_single_ptr(&mut self) -> PolarsResult<usize> {
509        polars_bail!(opq = as_single_ptr, self._dtype());
510    }
511
512    /// Shift the values by a given period and fill the parts that will be empty due to this operation
513    /// with `Nones`.
514    ///
515    /// *NOTE: If you want to fill the Nones with a value use the
516    /// [`shift` operation on `ChunkedArray<T>`](../chunked_array/ops/trait.ChunkShift.html).*
517    ///
518    /// # Example
519    ///
520    /// ```rust
521    /// # use polars_core::prelude::*;
522    /// fn example() -> PolarsResult<()> {
523    ///     let s = Series::new("series".into(), &[1, 2, 3]);
524    ///
525    ///     let shifted = s.shift(1);
526    ///     assert_eq!(Vec::from(shifted.i32()?), &[None, Some(1), Some(2)]);
527    ///
528    ///     let shifted = s.shift(-1);
529    ///     assert_eq!(Vec::from(shifted.i32()?), &[Some(2), Some(3), None]);
530    ///
531    ///     let shifted = s.shift(2);
532    ///     assert_eq!(Vec::from(shifted.i32()?), &[None, None, Some(1)]);
533    ///
534    ///     Ok(())
535    /// }
536    /// example();
537    /// ```
538    fn shift(&self, _periods: i64) -> Series;
539
540    /// Get the sum of the Series as a new Scalar.
541    ///
542    /// If the [`DataType`] is one of `{Int8, UInt8, Int16, UInt16}` the `Series` is
543    /// first cast to `Int64` to prevent overflow issues.
544    fn sum_reduce(&self) -> PolarsResult<Scalar> {
545        polars_bail!(opq = sum, self._dtype());
546    }
547    /// Get the max of the Series as a new Series of length 1.
548    fn max_reduce(&self) -> PolarsResult<Scalar> {
549        polars_bail!(opq = max, self._dtype());
550    }
551    /// Get the min of the Series as a new Series of length 1.
552    fn min_reduce(&self) -> PolarsResult<Scalar> {
553        polars_bail!(opq = min, self._dtype());
554    }
555    /// Get the median of the Series as a new Series of length 1.
556    fn median_reduce(&self) -> PolarsResult<Scalar> {
557        polars_bail!(opq = median, self._dtype());
558    }
559    /// Get the mean of the Series as a new Scalar
560    fn mean_reduce(&self) -> PolarsResult<Scalar> {
561        polars_bail!(opq = mean, self._dtype());
562    }
563    /// Get the variance of the Series as a new Series of length 1.
564    fn var_reduce(&self, _ddof: u8) -> PolarsResult<Scalar> {
565        polars_bail!(opq = var, self._dtype());
566    }
567    /// Get the standard deviation of the Series as a new Series of length 1.
568    fn std_reduce(&self, _ddof: u8) -> PolarsResult<Scalar> {
569        polars_bail!(opq = std, self._dtype());
570    }
571    /// Get the quantile of the Series as a new Series of length 1.
572    fn quantile_reduce(&self, _quantile: f64, _method: QuantileMethod) -> PolarsResult<Scalar> {
573        polars_bail!(opq = quantile, self._dtype());
574    }
575    /// Get multiple quantiles of the ChunkedArray as a new `List` Scalar
576    fn quantiles_reduce(
577        &self,
578        _quantiles: &[f64],
579        _method: QuantileMethod,
580    ) -> PolarsResult<Scalar> {
581        polars_bail!(opq = quantiles, self._dtype());
582    }
583    /// Get the bitwise AND of the Series as a new Series of length 1,
584    fn and_reduce(&self) -> PolarsResult<Scalar> {
585        polars_bail!(opq = and_reduce, self._dtype());
586    }
587    /// Get the bitwise OR of the Series as a new Series of length 1,
588    fn or_reduce(&self) -> PolarsResult<Scalar> {
589        polars_bail!(opq = or_reduce, self._dtype());
590    }
591    /// Get the bitwise XOR of the Series as a new Series of length 1,
592    fn xor_reduce(&self) -> PolarsResult<Scalar> {
593        polars_bail!(opq = xor_reduce, self._dtype());
594    }
595
596    /// Get the first element of the [`Series`] as a [`Scalar`]
597    ///
598    /// If the [`Series`] is empty, a [`Scalar`] with a [`AnyValue::Null`] is returned.
599    fn first(&self) -> Scalar {
600        let dt = self.dtype();
601        let av = self.get(0).map_or(AnyValue::Null, AnyValue::into_static);
602
603        Scalar::new(dt.clone(), av)
604    }
605
606    /// Get the first non-null element of the [`Series`] as a [`Scalar`]
607    ///
608    /// If the [`Series`] is empty, a [`Scalar`] with a [`AnyValue::Null`] is returned.
609    fn first_non_null(&self) -> Scalar {
610        let av = if self.len() == 0 {
611            AnyValue::Null
612        } else {
613            let idx = if self.has_nulls() {
614                first_non_null(self.chunks().iter().map(|c| c.as_ref())).unwrap_or(0)
615            } else {
616                0
617            };
618            self.get(idx).map_or(AnyValue::Null, AnyValue::into_static)
619        };
620        Scalar::new(self.dtype().clone(), av)
621    }
622
623    /// Get the last element of the [`Series`] as a [`Scalar`]
624    ///
625    /// If the [`Series`] is empty, a [`Scalar`] with a [`AnyValue::Null`] is returned.
626    fn last(&self) -> Scalar {
627        let dt = self.dtype();
628        let av = if self.len() == 0 {
629            AnyValue::Null
630        } else {
631            // SAFETY: len-1 < len if len != 0
632            unsafe { self.get_unchecked(self.len() - 1) }.into_static()
633        };
634
635        Scalar::new(dt.clone(), av)
636    }
637
638    /// Get the last non-null element of the [`Series`] as a [`Scalar`]
639    ///
640    /// If the [`Series`] is empty, a [`Scalar`] with a [`AnyValue::Null`] is returned.
641    fn last_non_null(&self) -> Scalar {
642        let n = self.len();
643        let av = if n == 0 {
644            AnyValue::Null
645        } else {
646            let idx = if self.has_nulls() {
647                last_non_null(self.chunks().iter().map(|c| c.as_ref()), n).unwrap_or(n - 1)
648            } else {
649                n - 1
650            };
651            // SAFETY: len-1 < len if len != 0
652            unsafe { self.get_unchecked(idx) }.into_static()
653        };
654        Scalar::new(self.dtype().clone(), av)
655    }
656
657    #[cfg(feature = "approx_unique")]
658    fn approx_n_unique(&self) -> PolarsResult<IdxSize> {
659        polars_bail!(opq = approx_n_unique, self._dtype());
660    }
661
662    /// Clone inner ChunkedArray and wrap in a new Arc
663    fn clone_inner(&self) -> Arc<dyn SeriesTrait>;
664
665    #[cfg(feature = "object")]
666    /// Get the value at this index as a downcastable Any trait ref.
667    fn get_object(&self, _index: usize) -> Option<&dyn PolarsObjectSafe> {
668        invalid_operation_panic!(get_object, self)
669    }
670
671    #[cfg(feature = "object")]
672    /// Get the value at this index as a downcastable Any trait ref.
673    ///
674    /// # Safety
675    /// This function doesn't do any bound checks.
676    unsafe fn get_object_chunked_unchecked(
677        &self,
678        _chunk: usize,
679        _index: usize,
680    ) -> Option<&dyn PolarsObjectSafe> {
681        invalid_operation_panic!(get_object_chunked_unchecked, self)
682    }
683
684    /// Get a hold of the [`ChunkedArray`], [`Logical`] or `NullChunked` as an `Any` trait
685    /// reference.
686    fn as_any(&self) -> &dyn Any;
687
688    /// Get a hold of the [`ChunkedArray`], [`Logical`] or `NullChunked` as an `Any` trait mutable
689    /// reference.
690    fn as_any_mut(&mut self) -> &mut dyn Any;
691
692    /// Get a hold of the [`ChunkedArray`] or `NullChunked` as an `Any` trait reference. This
693    /// pierces through `Logical` types to get the underlying physical array.
694    fn as_phys_any(&self) -> &dyn Any;
695
696    fn as_arc_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
697
698    #[cfg(feature = "checked_arithmetic")]
699    fn checked_div(&self, _rhs: &Series) -> PolarsResult<Series> {
700        polars_bail!(opq = checked_div, self._dtype());
701    }
702
703    #[cfg(feature = "rolling_window")]
704    /// Apply a custom function over a rolling/ moving window of the array.
705    /// This has quite some dynamic dispatch, so prefer rolling_min, max, mean, sum over this.
706    fn rolling_map(
707        &self,
708        _f: &dyn Fn(&Series) -> PolarsResult<Series>,
709        _options: RollingOptionsFixedWindow,
710    ) -> PolarsResult<Series> {
711        polars_bail!(opq = rolling_map, self._dtype());
712    }
713}
714
715impl dyn SeriesTrait + '_ {
716    pub fn unpack<T: PolarsPhysicalType>(&self) -> PolarsResult<&ChunkedArray<T>> {
717        polars_ensure!(&T::get_static_dtype() == self.dtype(), unpack);
718        Ok(self.as_ref())
719    }
720}