1#![allow(unsafe_op_in_unsafe_fn)]
2use crate::chunked_array::flags::StatisticsFlags;
4pub use crate::prelude::ChunkCompareEq;
5use crate::prelude::*;
6use crate::{HEAD_DEFAULT_LENGTH, TAIL_DEFAULT_LENGTH};
7
8macro_rules! invalid_operation_panic {
9 ($op:ident, $s:expr) => {
10 panic!(
11 "`{}` operation not supported for dtype `{}`",
12 stringify!($op),
13 $s._dtype()
14 )
15 };
16}
17
18pub mod amortized_iter;
19mod any_value;
20pub mod arithmetic;
21pub mod arrow_export;
22pub mod builder;
23
24mod comparison;
25mod from;
26pub mod implementations;
27pub(crate) mod iterator;
28pub mod ops;
29#[cfg(feature = "proptest")]
30pub mod proptest;
31mod series_trait;
32
33use std::borrow::Cow;
34use std::hash::{Hash, Hasher};
35use std::ops::Deref;
36
37pub use from::*;
38pub use iterator::SeriesIter;
39use num_traits::NumCast;
40use polars_arrow::compute::aggregate::estimated_bytes_size;
41use polars_error::feature_gated;
42use polars_utils::broadcast::BroadcastLength;
43use polars_utils::float::IsFloat;
44pub use series_trait::{IsSorted, *};
45
46use crate::chunked_array::cast::CastOptions;
47use crate::runtime::RAYON;
48#[cfg(feature = "zip_with")]
49use crate::series::arithmetic::coerce_lhs_rhs;
50use crate::utils::{Wrap, handle_casting_failures, materialize_dyn_int};
51
52#[derive(Clone)]
150#[must_use]
151pub struct Series(pub Arc<dyn SeriesTrait>);
152
153impl PartialEq for Wrap<Series> {
154 fn eq(&self, other: &Self) -> bool {
155 self.0.equals_missing(other)
156 }
157}
158
159impl Eq for Wrap<Series> {}
160
161impl Hash for Wrap<Series> {
162 fn hash<H: Hasher>(&self, state: &mut H) {
163 self.dtype().hash(state);
164 self.len().hash(state);
165
166 for av in self.iter() {
167 av.hash(state);
168 }
169 }
170}
171
172impl Series {
173 pub fn new_empty(name: PlSmallStr, dtype: &DataType) -> Series {
175 Series::full_null(name, 0, dtype)
176 }
177
178 pub fn clear(&self) -> Series {
179 if self.is_empty() {
180 self.clone()
181 } else {
182 match self.dtype() {
183 #[cfg(feature = "object")]
184 DataType::Object(_) => self
185 .take(&ChunkedArray::<IdxType>::new_vec(PlSmallStr::EMPTY, vec![]))
186 .unwrap(),
187 dt => Series::new_empty(self.name().clone(), dt),
188 }
189 }
190 }
191
192 #[doc(hidden)]
193 pub fn _get_inner_mut(&mut self) -> &mut dyn SeriesTrait {
194 if Arc::weak_count(&self.0) + Arc::strong_count(&self.0) != 1 {
195 self.0 = self.0.clone_inner();
196 }
197 Arc::get_mut(&mut self.0).expect("implementation error")
198 }
199
200 pub fn take_inner<T: PolarsPhysicalType>(self) -> ChunkedArray<T> {
202 let arc_any = self.0.as_arc_any();
203 let downcast = arc_any
204 .downcast::<implementations::SeriesWrap<ChunkedArray<T>>>()
205 .unwrap();
206
207 match Arc::try_unwrap(downcast) {
208 Ok(ca) => ca.0,
209 Err(ca) => ca.as_ref().as_ref().clone(),
210 }
211 }
212
213 #[inline]
215 pub fn array_ref(&self, chunk_idx: usize) -> &ArrayRef {
216 &self.chunks()[chunk_idx] as &ArrayRef
217 }
218
219 pub unsafe fn chunks_mut(&mut self) -> &mut Vec<ArrayRef> {
223 #[allow(unused_mut)]
224 let mut ca = self._get_inner_mut();
225 ca.chunks_mut()
226 }
227
228 pub fn into_chunks(mut self) -> Vec<ArrayRef> {
229 let ca = self._get_inner_mut();
230 let chunks = std::mem::take(unsafe { ca.chunks_mut() });
231 ca.compute_len();
232 chunks
233 }
234
235 pub fn select_chunk(&self, i: usize) -> Self {
237 let mut new = self.clear();
238 let mut flags = self.get_flags();
239
240 use StatisticsFlags as F;
241 flags &= F::IS_SORTED_ANY | F::CAN_FAST_EXPLODE_LIST;
242
243 let mut_new = new._get_inner_mut();
245 let chunks = unsafe { mut_new.chunks_mut() };
246 let chunk = self.chunks()[i].clone();
247 chunks.clear();
248 chunks.push(chunk);
249 mut_new.compute_len();
250 mut_new._set_flags(flags);
251 new
252 }
253
254 pub fn is_sorted_flag(&self) -> IsSorted {
255 if self.len() <= 1 {
256 return IsSorted::Ascending;
257 }
258 self.get_flags().is_sorted()
259 }
260
261 pub fn set_sorted_flag(&mut self, sorted: IsSorted) {
262 let mut flags = self.get_flags();
263 flags.set_sorted(sorted);
264 self.set_flags(flags);
265 }
266
267 pub(crate) fn clear_flags(&mut self) {
268 self.set_flags(StatisticsFlags::empty());
269 }
270 pub fn get_flags(&self) -> StatisticsFlags {
271 self.0._get_flags()
272 }
273
274 pub(crate) fn set_flags(&mut self, flags: StatisticsFlags) {
275 self._get_inner_mut()._set_flags(flags)
276 }
277
278 pub fn into_frame(self) -> DataFrame {
279 unsafe { DataFrame::new_unchecked(self.len(), vec![self.into()]) }
281 }
282
283 pub fn rename(&mut self, name: PlSmallStr) -> &mut Series {
285 self._get_inner_mut().rename(name);
286 self
287 }
288
289 pub fn with_name(mut self, name: PlSmallStr) -> Series {
291 self.rename(name);
292 self
293 }
294
295 pub fn from_arrow_chunks(name: PlSmallStr, arrays: Vec<ArrayRef>) -> PolarsResult<Series> {
296 Self::try_from((name, arrays))
297 }
298
299 pub fn from_arrow(name: PlSmallStr, array: ArrayRef) -> PolarsResult<Series> {
300 Self::try_from((name, array))
301 }
302
303 pub fn shrink_to_fit(&mut self) {
305 self._get_inner_mut().shrink_to_fit()
306 }
307
308 pub fn append(&mut self, other: &Series) -> PolarsResult<&mut Self> {
312 let must_cast = other.dtype().matches_schema_type(self.dtype())?;
313 if must_cast {
314 let other = other.cast(self.dtype())?;
315 self.append_owned(other)?;
316 } else {
317 self._get_inner_mut().append(other)?;
318 }
319 Ok(self)
320 }
321
322 pub fn append_owned(&mut self, other: Series) -> PolarsResult<&mut Self> {
326 let must_cast = other.dtype().matches_schema_type(self.dtype())?;
327 if must_cast {
328 let other = other.cast(self.dtype())?;
329 self._get_inner_mut().append_owned(other)?;
330 } else {
331 self._get_inner_mut().append_owned(other)?;
332 }
333 Ok(self)
334 }
335
336 pub fn compute_len(&mut self) {
338 self._get_inner_mut().compute_len()
339 }
340
341 pub fn extend(&mut self, other: &Series) -> PolarsResult<&mut Self> {
345 let must_cast = other.dtype().matches_schema_type(self.dtype())?;
346 if must_cast {
347 let other = other.cast(self.dtype())?;
348 self._get_inner_mut().extend(&other)?;
349 } else {
350 self._get_inner_mut().extend(other)?;
351 }
352 Ok(self)
353 }
354
355 pub fn broadcast_to(&self, length: usize) -> PolarsResult<Cow<'_, Self>> {
359 let len = self.len();
360 if len == length {
361 Ok(Cow::Borrowed(self))
362 } else if len == 1 {
363 Ok(Cow::Owned(self.new_from_index(0, length)))
364 } else {
365 polars_bail!(
366 ShapeMismatch: "can't broadcast Series '{}' of length {len} to length {length}",
367 self.name()
368 );
369 }
370 }
371
372 pub fn broadcast_in_place_to(&mut self, length: usize) -> PolarsResult<()> {
374 if let Cow::Owned(new) = self.broadcast_to(length)? {
375 *self = new;
376 }
377 Ok(())
378 }
379
380 pub fn broadcast_owned_to(mut self, length: usize) -> PolarsResult<Self> {
382 self.broadcast_in_place_to(length)?;
383 Ok(self)
384 }
385
386 pub fn sort(&self, sort_options: SortOptions) -> PolarsResult<Self> {
402 self.sort_with(sort_options)
403 }
404
405 pub fn as_single_ptr(&mut self) -> PolarsResult<usize> {
407 self._get_inner_mut().as_single_ptr()
408 }
409
410 pub fn cast(&self, dtype: &DataType) -> PolarsResult<Self> {
411 self.cast_with_options(dtype, CastOptions::NonStrict)
412 }
413
414 pub fn cast_with_options(&self, dtype: &DataType, options: CastOptions) -> PolarsResult<Self> {
416 let slf = self
417 .trim_lists_to_normalized_offsets()
418 .map_or(Cow::Borrowed(self), Cow::Owned);
419 let slf = slf.propagate_nulls().map_or(slf, Cow::Owned);
420
421 use DataType as D;
422 let do_clone = match dtype {
423 D::Unknown(UnknownKind::Any) => true,
424 D::Unknown(UnknownKind::Int(_)) if slf.dtype().is_integer() => true,
425 D::Unknown(UnknownKind::Float) if slf.dtype().is_float() => true,
426 D::Unknown(UnknownKind::Str)
427 if slf.dtype().is_string() | slf.dtype().is_categorical() =>
428 {
429 true
430 },
431 dt if (dt.is_primitive() || dt.is_extension()) && dt == slf.dtype() => true,
432 _ => false,
433 };
434
435 if do_clone {
436 return Ok(slf.into_owned());
437 }
438
439 pub fn cast_dtype(dtype: &DataType) -> Option<DataType> {
440 match dtype {
441 D::Unknown(UnknownKind::Int(v)) => Some(materialize_dyn_int(*v).dtype()),
442 D::Unknown(UnknownKind::Float) => Some(DataType::Float64),
443 D::Unknown(UnknownKind::Str) => Some(DataType::String),
444 D::List(inner) => cast_dtype(inner.as_ref()).map(Box::new).map(D::List),
446 #[cfg(feature = "dtype-struct")]
447 D::Struct(fields) => {
448 let mut field_iter = fields.iter().enumerate();
451 let mut new_fields = loop {
452 let (i, field) = field_iter.next()?;
453
454 if let Some(dtype) = cast_dtype(&field.dtype) {
455 let mut new_fields = Vec::with_capacity(fields.len());
456 new_fields.extend(fields.iter().take(i).cloned());
457 new_fields.push(Field {
458 name: field.name.clone(),
459 dtype,
460 });
461 break new_fields;
462 }
463 };
464
465 new_fields.extend(fields.iter().skip(new_fields.len()).cloned().map(|field| {
466 let dtype = cast_dtype(&field.dtype).unwrap_or(field.dtype);
467 Field {
468 name: field.name,
469 dtype,
470 }
471 }));
472
473 Some(D::Struct(new_fields))
474 },
475 _ => None,
476 }
477 }
478
479 let mut casted = cast_dtype(dtype);
480 if dtype.is_list() && dtype.inner_dtype().is_some_and(|dt| dt.is_null()) {
481 if let Some(from_inner_dtype) = slf.dtype().inner_dtype() {
482 casted = Some(DataType::List(Box::new(from_inner_dtype.clone())));
483 }
484 }
485 let dtype = match casted {
486 None => dtype,
487 Some(ref dtype) => dtype,
488 };
489
490 let len = slf.len();
492 if slf.null_count() == len {
493 return Ok(Series::full_null(slf.name().clone(), len, dtype));
494 }
495
496 let new_options = match options {
497 CastOptions::Strict if !dtype.is_nested() => CastOptions::NonStrict,
500 opt => opt,
501 };
502
503 let out = slf.0.cast(dtype, new_options)?;
504 if options.is_strict() {
505 handle_casting_failures(slf.as_ref(), &out)?;
506 }
507 Ok(out)
508 }
509
510 pub unsafe fn cast_unchecked(&self, dtype: &DataType) -> PolarsResult<Self> {
516 match self.dtype() {
517 #[cfg(feature = "dtype-struct")]
518 DataType::Struct(_) => self.struct_().unwrap().cast_unchecked(dtype),
519 DataType::List(_) => self.list().unwrap().cast_unchecked(dtype),
520 dt if dt.is_primitive_numeric() => {
521 with_match_physical_numeric_polars_type!(dt, |$T| {
522 let ca: &ChunkedArray<$T> = self.as_ref().as_ref().as_ref();
523 ca.cast_unchecked(dtype)
524 })
525 },
526 DataType::Binary => self.binary().unwrap().cast_unchecked(dtype),
527 _ => self.cast_with_options(dtype, CastOptions::Overflowing),
528 }
529 }
530
531 pub unsafe fn from_physical_unchecked(&self, dtype: &DataType) -> PolarsResult<Self> {
545 debug_assert!(!self.dtype().is_logical(), "{:?}", self.dtype());
546
547 if self.dtype() == dtype {
548 return Ok(self.clone());
549 }
550
551 use DataType as D;
552 match (self.dtype(), dtype) {
553 #[cfg(feature = "dtype-decimal")]
554 (D::Int128, D::Decimal(precision, scale)) => {
555 let ca = self.i128().unwrap();
556 Ok(ca
557 .clone()
558 .into_decimal_unchecked(*precision, *scale)
559 .into_series())
560 },
561
562 #[cfg(feature = "dtype-categorical")]
563 (phys, D::Categorical(cats, _)) if &cats.physical().dtype() == phys => {
564 with_match_categorical_physical_type!(cats.physical(), |$C| {
565 type CA = ChunkedArray<<$C as PolarsCategoricalType>::PolarsPhysical>;
566 let ca = self.as_ref().as_any().downcast_ref::<CA>().unwrap();
567 Ok(CategoricalChunked::<$C>::from_cats_and_dtype_unchecked(
568 ca.clone(),
569 dtype.clone(),
570 )
571 .into_series())
572 })
573 },
574 #[cfg(feature = "dtype-categorical")]
575 (phys, D::Enum(fcats, _)) if &fcats.physical().dtype() == phys => {
576 with_match_categorical_physical_type!(fcats.physical(), |$C| {
577 type CA = ChunkedArray<<$C as PolarsCategoricalType>::PolarsPhysical>;
578 let ca = self.as_ref().as_any().downcast_ref::<CA>().unwrap();
579 Ok(CategoricalChunked::<$C>::from_cats_and_dtype_unchecked(
580 ca.clone(),
581 dtype.clone(),
582 )
583 .into_series())
584 })
585 },
586
587 (D::Binary, D::BinaryOffset) => self.cast(&D::BinaryOffset),
589
590 (D::Int32, D::Date) => feature_gated!("dtype-date", Ok(self.clone().into_date())),
591 (D::Int64, D::Datetime(tu, tz)) => feature_gated!(
592 "dtype-datetime",
593 Ok(self.clone().into_datetime(*tu, tz.clone()))
594 ),
595 (D::Int64, D::Duration(tu)) => {
596 feature_gated!("dtype-duration", Ok(self.clone().into_duration(*tu)))
597 },
598 (D::Int64, D::Time) => feature_gated!("dtype-time", Ok(self.clone().into_time())),
599
600 (D::List(_), D::List(to)) => unsafe {
601 self.list()
602 .unwrap()
603 .from_physical_unchecked(to.as_ref().clone())
604 .map(|ca| ca.into_series())
605 },
606 #[cfg(feature = "dtype-array")]
607 (D::Array(_, lw), D::Array(to, rw)) if lw == rw => unsafe {
608 self.array()
609 .unwrap()
610 .from_physical_unchecked(to.as_ref().clone())
611 .map(|ca| ca.into_series())
612 },
613 #[cfg(feature = "dtype-struct")]
614 (D::Struct(_), D::Struct(to)) => unsafe {
615 self.struct_()
616 .unwrap()
617 .from_physical_unchecked(to.as_slice())
618 .map(|ca| ca.into_series())
619 },
620
621 #[cfg(feature = "dtype-map")]
622 (D::List(_), D::Map(_, _)) => {
623 use crate::chunked_array::logical::ensure_live_entries_non_null;
624
625 let storage = self.from_physical_unchecked(&dtype.map_storage_dtype().unwrap())?;
626 ensure_live_entries_non_null(storage.list().unwrap())?;
629 Ok(MapChunked::from_storage_unchecked(dtype.clone(), storage).into_series())
630 },
631 #[cfg(feature = "dtype-extension")]
632 (_, D::Extension(typ, storage)) => {
633 let storage_series = self.from_physical_unchecked(storage.as_ref())?;
634 let ext = ExtensionChunked::from_storage(typ.clone(), storage_series);
635 Ok(ext.into_series())
636 },
637
638 _ => panic!("invalid from_physical({dtype:?}) for {:?}", self.dtype()),
639 }
640 }
641
642 #[cfg(feature = "dtype-extension")]
643 pub fn into_extension(self, typ: ExtensionTypeInstance) -> Series {
644 assert!(!self.dtype().is_extension());
645 let ext = ExtensionChunked::from_storage(typ, self);
646 ext.into_series()
647 }
648
649 pub fn to_float(&self) -> PolarsResult<Series> {
651 match self.dtype() {
652 DataType::Float32 | DataType::Float64 => Ok(self.clone()),
653 _ => self.cast_with_options(&DataType::Float64, CastOptions::Overflowing),
654 }
655 }
656
657 pub fn sum<T>(&self) -> PolarsResult<T>
664 where
665 T: NumCast + IsFloat,
666 {
667 let sum = self.sum_reduce()?;
668 let sum = sum.value().extract().unwrap();
669 Ok(sum)
670 }
671
672 pub fn min<T>(&self) -> PolarsResult<Option<T>>
675 where
676 T: NumCast + IsFloat,
677 {
678 let min = self.min_reduce()?;
679 let min = min.value().extract::<T>();
680 Ok(min)
681 }
682
683 pub fn max<T>(&self) -> PolarsResult<Option<T>>
686 where
687 T: NumCast + IsFloat,
688 {
689 let max = self.max_reduce()?;
690 let max = max.value().extract::<T>();
691 Ok(max)
692 }
693
694 pub fn explode(&self, options: ExplodeOptions) -> PolarsResult<Series> {
696 match self.dtype() {
697 DataType::List(_) => self.list().unwrap().explode(options),
698 #[cfg(feature = "dtype-array")]
699 DataType::Array(_, _) => self.array().unwrap().explode(options),
700 _ => Ok(self.clone()),
701 }
702 }
703
704 pub fn is_nan(&self) -> PolarsResult<BooleanChunked> {
706 match self.dtype() {
707 #[cfg(feature = "dtype-f16")]
708 DataType::Float16 => Ok(self.f16().unwrap().is_nan()),
709 DataType::Float32 => Ok(self.f32().unwrap().is_nan()),
710 DataType::Float64 => Ok(self.f64().unwrap().is_nan()),
711 DataType::Null => Ok(BooleanChunked::full_null(self.name().clone(), self.len())),
712 dt if dt.is_primitive_numeric() => {
713 let arr = BooleanArray::full(self.len(), false, ArrowDataType::Boolean)
714 .with_validity(self.rechunk_validity());
715 Ok(BooleanChunked::with_chunk(self.name().clone(), arr))
716 },
717 _ => polars_bail!(opq = is_nan, self.dtype()),
718 }
719 }
720
721 pub fn is_not_nan(&self) -> PolarsResult<BooleanChunked> {
723 match self.dtype() {
724 #[cfg(feature = "dtype-f16")]
725 DataType::Float16 => Ok(self.f16().unwrap().is_not_nan()),
726 DataType::Float32 => Ok(self.f32().unwrap().is_not_nan()),
727 DataType::Float64 => Ok(self.f64().unwrap().is_not_nan()),
728 DataType::Null => Ok(BooleanChunked::full_null(self.name().clone(), self.len())),
729 dt if dt.is_primitive_numeric() => {
730 let arr = BooleanArray::full(self.len(), true, ArrowDataType::Boolean)
731 .with_validity(self.rechunk_validity());
732 Ok(BooleanChunked::with_chunk(self.name().clone(), arr))
733 },
734 _ => polars_bail!(opq = is_not_nan, self.dtype()),
735 }
736 }
737
738 pub fn is_finite(&self) -> PolarsResult<BooleanChunked> {
740 match self.dtype() {
741 #[cfg(feature = "dtype-f16")]
742 DataType::Float16 => Ok(self.f16().unwrap().is_finite()),
743 DataType::Float32 => Ok(self.f32().unwrap().is_finite()),
744 DataType::Float64 => Ok(self.f64().unwrap().is_finite()),
745 DataType::Null => Ok(BooleanChunked::full_null(self.name().clone(), self.len())),
746 dt if dt.is_primitive_numeric() => {
747 let arr = BooleanArray::full(self.len(), true, ArrowDataType::Boolean)
748 .with_validity(self.rechunk_validity());
749 Ok(BooleanChunked::with_chunk(self.name().clone(), arr))
750 },
751 _ => polars_bail!(opq = is_finite, self.dtype()),
752 }
753 }
754
755 pub fn is_infinite(&self) -> PolarsResult<BooleanChunked> {
757 match self.dtype() {
758 #[cfg(feature = "dtype-f16")]
759 DataType::Float16 => Ok(self.f16().unwrap().is_infinite()),
760 DataType::Float32 => Ok(self.f32().unwrap().is_infinite()),
761 DataType::Float64 => Ok(self.f64().unwrap().is_infinite()),
762 DataType::Null => Ok(BooleanChunked::full_null(self.name().clone(), self.len())),
763 dt if dt.is_primitive_numeric() => {
764 let arr = BooleanArray::full(self.len(), false, ArrowDataType::Boolean)
765 .with_validity(self.rechunk_validity());
766 Ok(BooleanChunked::with_chunk(self.name().clone(), arr))
767 },
768 _ => polars_bail!(opq = is_infinite, self.dtype()),
769 }
770 }
771
772 #[cfg(feature = "zip_with")]
776 pub fn zip_with(&self, mask: &BooleanChunked, other: &Series) -> PolarsResult<Series> {
777 let (lhs, rhs) = coerce_lhs_rhs(self, other)?;
778 lhs.zip_with_same_type(mask, rhs.as_ref())
779 }
780
781 pub fn to_physical_repr(&self) -> Cow<'_, Series> {
795 use DataType::*;
796 match self.dtype() {
797 #[cfg(feature = "dtype-date")]
800 Date => Cow::Owned(self.date().unwrap().phys.clone().into_series()),
801 #[cfg(feature = "dtype-datetime")]
802 Datetime(_, _) => Cow::Owned(self.datetime().unwrap().phys.clone().into_series()),
803 #[cfg(feature = "dtype-duration")]
804 Duration(_) => Cow::Owned(self.duration().unwrap().phys.clone().into_series()),
805 #[cfg(feature = "dtype-time")]
806 Time => Cow::Owned(self.time().unwrap().phys.clone().into_series()),
807 #[cfg(feature = "dtype-categorical")]
808 dt @ (Categorical(_, _) | Enum(_, _)) => {
809 with_match_categorical_physical_type!(dt.cat_physical().unwrap(), |$C| {
810 let ca = self.cat::<$C>().unwrap();
811 Cow::Owned(ca.physical().clone().into_series())
812 })
813 },
814 #[cfg(feature = "dtype-decimal")]
815 Decimal(_, _) => Cow::Owned(self.decimal().unwrap().phys.clone().into_series()),
816 List(_) => match self.list().unwrap().to_physical_repr() {
817 Cow::Borrowed(_) => Cow::Borrowed(self),
818 Cow::Owned(ca) => Cow::Owned(ca.into_series()),
819 },
820 #[cfg(feature = "dtype-array")]
821 Array(_, _) => match self.array().unwrap().to_physical_repr() {
822 Cow::Borrowed(_) => Cow::Borrowed(self),
823 Cow::Owned(ca) => Cow::Owned(ca.into_series()),
824 },
825 #[cfg(feature = "dtype-struct")]
826 Struct(_) => match self.struct_().unwrap().to_physical_repr() {
827 Cow::Borrowed(_) => Cow::Borrowed(self),
828 Cow::Owned(ca) => Cow::Owned(ca.into_series()),
829 },
830 #[cfg(feature = "dtype-map")]
831 Map(_, _) => match self.map().unwrap().storage().to_physical_repr() {
832 Cow::Borrowed(storage) => Cow::Owned(storage.clone()),
833 Cow::Owned(storage) => Cow::Owned(storage),
834 },
835 #[cfg(feature = "dtype-extension")]
836 Extension(_, _) => self.ext().unwrap().storage().to_physical_repr(),
837 _ => Cow::Borrowed(self),
838 }
839 }
840
841 pub fn to_storage(&self) -> &Series {
844 #[cfg(feature = "dtype-extension")]
845 {
846 if let DataType::Extension(_, _) = self.dtype() {
847 return self.ext().unwrap().storage();
848 }
849 }
850 self
851 }
852
853 pub fn gather_every(&self, n: usize, offset: usize) -> PolarsResult<Series> {
855 polars_ensure!(n > 0, ComputeError: "cannot perform gather every for `n=0`");
856 let idx = ((offset as IdxSize)..self.len() as IdxSize)
857 .step_by(n)
858 .collect_ca(PlSmallStr::EMPTY);
859 Ok(unsafe { self.take_unchecked(&idx) })
861 }
862
863 #[cfg(feature = "dot_product")]
864 pub fn dot(&self, other: &Series) -> PolarsResult<f64> {
865 std::ops::Mul::mul(self, other)?.sum::<f64>()
866 }
867
868 pub fn sum_reduce(&self) -> PolarsResult<Scalar> {
875 self.0.sum_reduce()
876 }
877
878 pub fn mean_reduce(&self) -> PolarsResult<Scalar> {
881 self.0.mean_reduce()
882 }
883
884 pub fn product(&self) -> PolarsResult<Scalar> {
889 #[cfg(feature = "product")]
890 {
891 use DataType::*;
892 match self.dtype() {
893 Boolean => self.cast(&DataType::Int64).unwrap().product(),
894 Int8 | UInt8 | Int16 | UInt16 | Int32 | UInt32 => {
895 let s = self.cast(&Int64).unwrap();
896 s.product()
897 },
898 Int64 => Ok(self.i64().unwrap().prod_reduce()),
899 UInt64 => Ok(self.u64().unwrap().prod_reduce()),
900 #[cfg(feature = "dtype-i128")]
901 Int128 => Ok(self.i128().unwrap().prod_reduce()),
902 #[cfg(feature = "dtype-u128")]
903 UInt128 => Ok(self.u128().unwrap().prod_reduce()),
904 #[cfg(feature = "dtype-f16")]
905 Float16 => Ok(self.f16().unwrap().prod_reduce()),
906 Float32 => Ok(self.f32().unwrap().prod_reduce()),
907 Float64 => Ok(self.f64().unwrap().prod_reduce()),
908 #[cfg(feature = "dtype-decimal")]
909 Decimal(..) => Ok(self.decimal().unwrap().prod_reduce()),
910 dt => {
911 polars_bail!(InvalidOperation: "`product` operation not supported for dtype `{dt}`")
912 },
913 }
914 }
915 #[cfg(not(feature = "product"))]
916 {
917 panic!("activate 'product' feature")
918 }
919 }
920
921 pub fn strict_cast(&self, dtype: &DataType) -> PolarsResult<Series> {
923 self.cast_with_options(dtype, CastOptions::Strict)
924 }
925
926 #[doc(hidden)]
931 pub fn _cast_reporting_inexact(
932 &self,
933 dtype: &DataType,
934 ) -> PolarsResult<(Series, Option<BooleanChunked>)> {
935 let casted = self.cast_with_options(dtype, CastOptions::NonStrict)?;
936 if self.dtype().is_integer() && (dtype.is_integer() || dtype.is_decimal()) {
939 if casted.null_count() == self.null_count() {
940 return Ok((casted, None));
941 }
942 let inexact = &casted.is_null() & &self.is_not_null();
943 return Ok((casted, Some(inexact)));
944 }
945 let roundtrip = casted.cast_with_options(self.dtype(), CastOptions::NonStrict)?;
947 let inexact = roundtrip.not_equal_missing(self)?;
948 debug_assert_eq!(inexact.null_count(), 0);
949 Ok((casted, inexact.any().then_some(inexact)))
950 }
951
952 #[cfg(feature = "dtype-decimal")]
953 pub fn into_decimal(self, precision: usize, scale: usize) -> PolarsResult<Series> {
954 match self.dtype() {
955 DataType::Int128 => Ok(self
956 .i128()
957 .unwrap()
958 .clone()
959 .into_decimal(precision, scale)?
960 .into_series()),
961 DataType::Decimal(cur_prec, cur_scale)
962 if scale == *cur_scale && precision >= *cur_prec =>
963 {
964 Ok(self)
965 },
966 dt => panic!("into_decimal({precision:?}, {scale}) not implemented for {dt:?}"),
967 }
968 }
969
970 #[cfg(feature = "dtype-time")]
971 pub fn into_time(self) -> Series {
972 match self.dtype() {
973 DataType::Int64 => self.i64().unwrap().clone().into_time().into_series(),
974 DataType::Time => self
975 .time()
976 .unwrap()
977 .physical()
978 .clone()
979 .into_time()
980 .into_series(),
981 dt => panic!("date not implemented for {dt:?}"),
982 }
983 }
984
985 pub fn into_date(self) -> Series {
986 #[cfg(not(feature = "dtype-date"))]
987 {
988 panic!("activate feature dtype-date")
989 }
990 #[cfg(feature = "dtype-date")]
991 match self.dtype() {
992 DataType::Int32 => self.i32().unwrap().clone().into_date().into_series(),
993 DataType::Date => self
994 .date()
995 .unwrap()
996 .physical()
997 .clone()
998 .into_date()
999 .into_series(),
1000 dt => panic!("date not implemented for {dt:?}"),
1001 }
1002 }
1003
1004 #[allow(unused_variables)]
1005 pub fn into_datetime(self, timeunit: TimeUnit, tz: Option<TimeZone>) -> Series {
1006 #[cfg(not(feature = "dtype-datetime"))]
1007 {
1008 panic!("activate feature dtype-datetime")
1009 }
1010
1011 #[cfg(feature = "dtype-datetime")]
1012 match self.dtype() {
1013 DataType::Int64 => self
1014 .i64()
1015 .unwrap()
1016 .clone()
1017 .into_datetime(timeunit, tz)
1018 .into_series(),
1019 DataType::Datetime(_, _) => self
1020 .datetime()
1021 .unwrap()
1022 .physical()
1023 .clone()
1024 .into_datetime(timeunit, tz)
1025 .into_series(),
1026 dt => panic!("into_datetime not implemented for {dt:?}"),
1027 }
1028 }
1029
1030 #[allow(unused_variables)]
1031 pub fn into_duration(self, timeunit: TimeUnit) -> Series {
1032 #[cfg(not(feature = "dtype-duration"))]
1033 {
1034 panic!("activate feature dtype-duration")
1035 }
1036 #[cfg(feature = "dtype-duration")]
1037 match self.dtype() {
1038 DataType::Int64 => self
1039 .i64()
1040 .unwrap()
1041 .clone()
1042 .into_duration(timeunit)
1043 .into_series(),
1044 DataType::Duration(_) => self
1045 .duration()
1046 .unwrap()
1047 .physical()
1048 .clone()
1049 .into_duration(timeunit)
1050 .into_series(),
1051 dt => panic!("into_duration not implemented for {dt:?}"),
1052 }
1053 }
1054
1055 pub fn str_value(&self, index: usize) -> PolarsResult<Cow<'_, str>> {
1057 Ok(self.0.get(index)?.str_value())
1058 }
1059 pub fn head(&self, length: Option<usize>) -> Series {
1061 let len = length.unwrap_or(HEAD_DEFAULT_LENGTH);
1062 self.slice(0, std::cmp::min(len, self.len()))
1063 }
1064
1065 pub fn tail(&self, length: Option<usize>) -> Series {
1067 let len = length.unwrap_or(TAIL_DEFAULT_LENGTH);
1068 let len = std::cmp::min(len, self.len());
1069 self.slice(-(len as i64), len)
1070 }
1071
1072 pub fn unique_stable(&self) -> PolarsResult<Series> {
1075 let idx = self.arg_unique()?;
1076 unsafe { Ok(self.take_unchecked(&idx)) }
1078 }
1079
1080 pub fn try_idx(&self) -> Option<&IdxCa> {
1081 #[cfg(feature = "bigidx")]
1082 {
1083 self.try_u64()
1084 }
1085 #[cfg(not(feature = "bigidx"))]
1086 {
1087 self.try_u32()
1088 }
1089 }
1090
1091 pub fn idx(&self) -> PolarsResult<&IdxCa> {
1092 #[cfg(feature = "bigidx")]
1093 {
1094 self.u64()
1095 }
1096 #[cfg(not(feature = "bigidx"))]
1097 {
1098 self.u32()
1099 }
1100 }
1101
1102 pub fn estimated_size(&self) -> usize {
1115 let mut size = 0;
1116 match self.dtype() {
1117 #[cfg(feature = "object")]
1119 DataType::Object(_) => {
1120 let ArrowDataType::FixedSizeBinary(size) = self.chunks()[0].dtype() else {
1121 unreachable!()
1122 };
1123 return self.len() * *size;
1125 },
1126 _ => {},
1127 }
1128
1129 size += self
1130 .chunks()
1131 .iter()
1132 .map(|arr| estimated_bytes_size(&**arr))
1133 .sum::<usize>();
1134
1135 size
1136 }
1137
1138 pub fn row_encode_unordered(&self) -> PolarsResult<BinaryOffsetChunked> {
1139 row_encode::_get_rows_encoded_ca_unordered(
1140 self.name().clone(),
1141 &[self.clone().into_column()],
1142 )
1143 }
1144
1145 pub fn row_encode_ordered(
1146 &self,
1147 descending: bool,
1148 nulls_last: bool,
1149 ) -> PolarsResult<BinaryOffsetChunked> {
1150 row_encode::_get_rows_encoded_ca(
1151 self.name().clone(),
1152 &[self.clone().into_column()],
1153 &[descending],
1154 &[nulls_last],
1155 false,
1156 )
1157 }
1158}
1159
1160impl Default for Series {
1161 fn default() -> Self {
1162 NullChunked::new(PlSmallStr::EMPTY, 0).into_series()
1163 }
1164}
1165
1166impl Deref for Series {
1167 type Target = dyn SeriesTrait;
1168
1169 #[inline(always)]
1170 fn deref(&self) -> &Self::Target {
1171 self.0.as_ref()
1172 }
1173}
1174
1175impl<'a> AsRef<dyn SeriesTrait + 'a> for Series {
1176 fn as_ref(&self) -> &(dyn SeriesTrait + 'a) {
1177 self.0.as_ref()
1178 }
1179}
1180
1181impl<T: PolarsPhysicalType> AsRef<ChunkedArray<T>> for dyn SeriesTrait + '_ {
1182 fn as_ref(&self) -> &ChunkedArray<T> {
1183 let Some(ca) = self.as_any().downcast_ref::<ChunkedArray<T>>() else {
1186 panic!(
1187 "implementation error, cannot get ref {:?} from {:?}",
1188 T::get_static_dtype(),
1189 self.dtype()
1190 );
1191 };
1192
1193 ca
1194 }
1195}
1196
1197impl<T: PolarsPhysicalType> AsMut<ChunkedArray<T>> for dyn SeriesTrait + '_ {
1198 fn as_mut(&mut self) -> &mut ChunkedArray<T> {
1199 if !self.as_any_mut().is::<ChunkedArray<T>>() {
1200 panic!(
1201 "implementation error, cannot get ref {:?} from {:?}",
1202 T::get_static_dtype(),
1203 self.dtype()
1204 );
1205 }
1206
1207 self.as_any_mut().downcast_mut::<ChunkedArray<T>>().unwrap()
1210 }
1211}
1212
1213impl BroadcastLength for Series {
1214 fn _broadcast_len(&self) -> usize {
1215 self.len()
1216 }
1217
1218 fn _column_name(&self) -> Option<&str> {
1219 Some(self.name())
1220 }
1221}
1222
1223#[cfg(test)]
1224mod test {
1225 use crate::series::*;
1226
1227 #[test]
1228 fn cast() {
1229 let ar = UInt32Chunked::new("a".into(), &[1, 2]);
1230 let s = ar.into_series();
1231 let s2 = s.cast(&DataType::Int64).unwrap();
1232
1233 assert!(s2.i64().is_ok());
1234 let s2 = s.cast(&DataType::Float32).unwrap();
1235 assert!(s2.f32().is_ok());
1236 }
1237
1238 #[test]
1239 fn new_series() {
1240 let _ = Series::new("boolean series".into(), &vec![true, false, true]);
1241 let _ = Series::new("int series".into(), &[1, 2, 3]);
1242 let ca = Int32Chunked::new("a".into(), &[1, 2, 3]);
1243 let _ = ca.into_series();
1244 }
1245
1246 #[test]
1247 #[cfg(feature = "dtype-date")]
1248 fn roundtrip_list_logical_20311() {
1249 let list = ListChunked::from_chunk_iter(
1250 PlSmallStr::from_static("a"),
1251 [ListArray::new(
1252 ArrowDataType::LargeList(Box::new(ArrowField::new(
1253 LIST_VALUES_NAME,
1254 ArrowDataType::Int32,
1255 true,
1256 ))),
1257 unsafe { polars_arrow::offset::Offsets::new_unchecked(vec![0, 1]) }.into(),
1258 PrimitiveArray::new(ArrowDataType::Int32, vec![1i32].into(), None).to_boxed(),
1259 None,
1260 )],
1261 );
1262 let list = unsafe { list.from_physical_unchecked(DataType::Date) }.unwrap();
1263 assert_eq!(list.dtype(), &DataType::List(Box::new(DataType::Date)));
1264 }
1265
1266 #[test]
1267 #[cfg(feature = "dtype-struct")]
1268 fn new_series_from_empty_structs() {
1269 let dtype = DataType::Struct(vec![]);
1270 let empties = vec![AnyValue::StructOwned(Box::new((vec![], vec![]))); 3];
1271 let s = Series::from_any_values_and_dtype("".into(), &empties, &dtype, false).unwrap();
1272 assert_eq!(s.len(), 3);
1273 }
1274 #[test]
1275 fn new_series_from_arrow_primitive_array() {
1276 let array = UInt32Array::from_slice([1, 2, 3, 4, 5]);
1277 let array_ref: ArrayRef = Box::new(array);
1278
1279 let _ = Series::try_new("foo".into(), array_ref).unwrap();
1280 }
1281
1282 #[test]
1283 fn series_append() {
1284 let mut s1 = Series::new("a".into(), &[1, 2]);
1285 let s2 = Series::new("b".into(), &[3]);
1286 s1.append(&s2).unwrap();
1287 assert_eq!(s1.len(), 3);
1288
1289 let s2 = Series::new("b".into(), &[3.0]);
1291 assert!(s1.append(&s2).is_err())
1292 }
1293
1294 #[test]
1295 #[cfg(feature = "dtype-decimal")]
1296 fn series_append_decimal() {
1297 let s1 = Series::new("a".into(), &[1.1, 2.3])
1298 .cast(&DataType::Decimal(38, 2))
1299 .unwrap();
1300 let s2 = Series::new("b".into(), &[3])
1301 .cast(&DataType::Decimal(38, 0))
1302 .unwrap();
1303
1304 {
1305 let mut s1 = s1.clone();
1306 s1.append(&s2).unwrap();
1307 assert_eq!(s1.len(), 3);
1308 assert_eq!(s1.get(2).unwrap(), AnyValue::Decimal(300, 38, 2));
1309 }
1310
1311 {
1312 let mut s2 = s2;
1313 s2.extend(&s1).unwrap();
1314 assert_eq!(s2.get(2).unwrap(), AnyValue::Decimal(2, 38, 0));
1315 }
1316 }
1317
1318 #[test]
1319 fn series_slice_works() {
1320 let series = Series::new("a".into(), &[1i64, 2, 3, 4, 5]);
1321
1322 let slice_1 = series.slice(-3, 3);
1323 let slice_2 = series.slice(-5, 5);
1324 let slice_3 = series.slice(0, 5);
1325
1326 assert_eq!(slice_1.get(0).unwrap(), AnyValue::Int64(3));
1327 assert_eq!(slice_2.get(0).unwrap(), AnyValue::Int64(1));
1328 assert_eq!(slice_3.get(0).unwrap(), AnyValue::Int64(1));
1329 }
1330
1331 #[test]
1332 fn out_of_range_slice_does_not_panic() {
1333 let series = Series::new("a".into(), &[1i64, 2, 3, 4, 5]);
1334
1335 let _ = series.slice(-3, 4);
1336 let _ = series.slice(-6, 2);
1337 let _ = series.slice(4, 2);
1338 }
1339}