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 builder;
22#[cfg(feature = "dtype-categorical")]
23pub mod categorical_to_arrow;
24mod comparison;
25mod from;
26pub mod implementations;
27mod into;
28pub use into::ToArrowConverter;
29pub(crate) mod iterator;
30pub mod ops;
31#[cfg(feature = "proptest")]
32pub mod proptest;
33mod series_trait;
34
35use std::borrow::Cow;
36use std::hash::{Hash, Hasher};
37use std::ops::Deref;
38
39use arrow::compute::aggregate::estimated_bytes_size;
40use arrow::offset::Offsets;
41pub use from::*;
42pub use iterator::{SeriesIter, SeriesPhysIter};
43use num_traits::NumCast;
44use polars_error::feature_gated;
45use polars_utils::float::IsFloat;
46pub use series_trait::{IsSorted, *};
47
48use crate::POOL;
49use crate::chunked_array::cast::CastOptions;
50#[cfg(feature = "zip_with")]
51use crate::series::arithmetic::coerce_lhs_rhs;
52use crate::utils::{Wrap, handle_casting_failures, materialize_dyn_int};
53
54#[derive(Clone)]
152#[must_use]
153pub struct Series(pub Arc<dyn SeriesTrait>);
154
155impl PartialEq for Wrap<Series> {
156 fn eq(&self, other: &Self) -> bool {
157 self.0.equals_missing(other)
158 }
159}
160
161impl Eq for Wrap<Series> {}
162
163impl Hash for Wrap<Series> {
164 fn hash<H: Hasher>(&self, state: &mut H) {
165 let rs = PlSeedableRandomStateQuality::fixed();
166 let mut h = vec![];
167 if self.0.vec_hash(rs, &mut h).is_ok() {
168 let h = h.into_iter().fold(0, |a: u64, b| a.wrapping_add(b));
169 h.hash(state)
170 } else {
171 self.len().hash(state);
172 self.null_count().hash(state);
173 self.dtype().hash(state);
174 }
175 }
176}
177
178impl Series {
179 pub fn new_empty(name: PlSmallStr, dtype: &DataType) -> Series {
181 Series::full_null(name, 0, dtype)
182 }
183
184 pub fn clear(&self) -> Series {
185 if self.is_empty() {
186 self.clone()
187 } else {
188 match self.dtype() {
189 #[cfg(feature = "object")]
190 DataType::Object(_) => self
191 .take(&ChunkedArray::<IdxType>::new_vec(PlSmallStr::EMPTY, vec![]))
192 .unwrap(),
193 dt => Series::new_empty(self.name().clone(), dt),
194 }
195 }
196 }
197
198 #[doc(hidden)]
199 pub fn _get_inner_mut(&mut self) -> &mut dyn SeriesTrait {
200 if Arc::weak_count(&self.0) + Arc::strong_count(&self.0) != 1 {
201 self.0 = self.0.clone_inner();
202 }
203 Arc::get_mut(&mut self.0).expect("implementation error")
204 }
205
206 pub fn take_inner<T: PolarsPhysicalType>(self) -> ChunkedArray<T> {
208 let arc_any = self.0.as_arc_any();
209 let downcast = arc_any
210 .downcast::<implementations::SeriesWrap<ChunkedArray<T>>>()
211 .unwrap();
212
213 match Arc::try_unwrap(downcast) {
214 Ok(ca) => ca.0,
215 Err(ca) => ca.as_ref().as_ref().clone(),
216 }
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 sort(&self, sort_options: SortOptions) -> PolarsResult<Self> {
371 self.sort_with(sort_options)
372 }
373
374 pub fn as_single_ptr(&mut self) -> PolarsResult<usize> {
376 self._get_inner_mut().as_single_ptr()
377 }
378
379 pub fn cast(&self, dtype: &DataType) -> PolarsResult<Self> {
380 self.cast_with_options(dtype, CastOptions::NonStrict)
381 }
382
383 pub fn cast_with_options(&self, dtype: &DataType, options: CastOptions) -> PolarsResult<Self> {
385 let slf = self
386 .trim_lists_to_normalized_offsets()
387 .map_or(Cow::Borrowed(self), Cow::Owned);
388 let slf = slf.propagate_nulls().map_or(slf, Cow::Owned);
389
390 use DataType as D;
391 let do_clone = match dtype {
392 D::Unknown(UnknownKind::Any) => true,
393 D::Unknown(UnknownKind::Int(_)) if slf.dtype().is_integer() => true,
394 D::Unknown(UnknownKind::Float) if slf.dtype().is_float() => true,
395 D::Unknown(UnknownKind::Str)
396 if slf.dtype().is_string() | slf.dtype().is_categorical() =>
397 {
398 true
399 },
400 dt if (dt.is_primitive() || dt.is_extension()) && dt == slf.dtype() => true,
401 _ => false,
402 };
403
404 if do_clone {
405 return Ok(slf.into_owned());
406 }
407
408 pub fn cast_dtype(dtype: &DataType) -> Option<DataType> {
409 match dtype {
410 D::Unknown(UnknownKind::Int(v)) => Some(materialize_dyn_int(*v).dtype()),
411 D::Unknown(UnknownKind::Float) => Some(DataType::Float64),
412 D::Unknown(UnknownKind::Str) => Some(DataType::String),
413 D::List(inner) => cast_dtype(inner.as_ref()).map(Box::new).map(D::List),
415 #[cfg(feature = "dtype-struct")]
416 D::Struct(fields) => {
417 let mut field_iter = fields.iter().enumerate();
420 let mut new_fields = loop {
421 let (i, field) = field_iter.next()?;
422
423 if let Some(dtype) = cast_dtype(&field.dtype) {
424 let mut new_fields = Vec::with_capacity(fields.len());
425 new_fields.extend(fields.iter().take(i).cloned());
426 new_fields.push(Field {
427 name: field.name.clone(),
428 dtype,
429 });
430 break new_fields;
431 }
432 };
433
434 new_fields.extend(fields.iter().skip(new_fields.len()).cloned().map(|field| {
435 let dtype = cast_dtype(&field.dtype).unwrap_or(field.dtype);
436 Field {
437 name: field.name,
438 dtype,
439 }
440 }));
441
442 Some(D::Struct(new_fields))
443 },
444 _ => None,
445 }
446 }
447
448 let mut casted = cast_dtype(dtype);
449 if dtype.is_list() && dtype.inner_dtype().is_some_and(|dt| dt.is_null()) {
450 if let Some(from_inner_dtype) = slf.dtype().inner_dtype() {
451 casted = Some(DataType::List(Box::new(from_inner_dtype.clone())));
452 }
453 }
454 let dtype = match casted {
455 None => dtype,
456 Some(ref dtype) => dtype,
457 };
458
459 let len = slf.len();
461 if slf.null_count() == len {
462 return Ok(Series::full_null(slf.name().clone(), len, dtype));
463 }
464
465 let new_options = match options {
466 CastOptions::Strict if !dtype.is_nested() => CastOptions::NonStrict,
469 opt => opt,
470 };
471
472 let out = slf.0.cast(dtype, new_options)?;
473 if options.is_strict() {
474 handle_casting_failures(slf.as_ref(), &out)?;
475 }
476 Ok(out)
477 }
478
479 pub unsafe fn cast_unchecked(&self, dtype: &DataType) -> PolarsResult<Self> {
485 match self.dtype() {
486 #[cfg(feature = "dtype-struct")]
487 DataType::Struct(_) => self.struct_().unwrap().cast_unchecked(dtype),
488 DataType::List(_) => self.list().unwrap().cast_unchecked(dtype),
489 dt if dt.is_primitive_numeric() => {
490 with_match_physical_numeric_polars_type!(dt, |$T| {
491 let ca: &ChunkedArray<$T> = self.as_ref().as_ref().as_ref();
492 ca.cast_unchecked(dtype)
493 })
494 },
495 DataType::Binary => self.binary().unwrap().cast_unchecked(dtype),
496 _ => self.cast_with_options(dtype, CastOptions::Overflowing),
497 }
498 }
499
500 pub unsafe fn from_physical_unchecked(&self, dtype: &DataType) -> PolarsResult<Self> {
506 debug_assert!(!self.dtype().is_logical(), "{:?}", self.dtype());
507
508 if self.dtype() == dtype {
509 return Ok(self.clone());
510 }
511
512 use DataType as D;
513 match (self.dtype(), dtype) {
514 #[cfg(feature = "dtype-decimal")]
515 (D::Int128, D::Decimal(precision, scale)) => {
516 let ca = self.i128().unwrap();
517 Ok(ca
518 .clone()
519 .into_decimal_unchecked(*precision, *scale)
520 .into_series())
521 },
522
523 #[cfg(feature = "dtype-categorical")]
524 (phys, D::Categorical(cats, _)) if &cats.physical().dtype() == phys => {
525 with_match_categorical_physical_type!(cats.physical(), |$C| {
526 type CA = ChunkedArray<<$C as PolarsCategoricalType>::PolarsPhysical>;
527 let ca = self.as_ref().as_any().downcast_ref::<CA>().unwrap();
528 Ok(CategoricalChunked::<$C>::from_cats_and_dtype_unchecked(
529 ca.clone(),
530 dtype.clone(),
531 )
532 .into_series())
533 })
534 },
535 #[cfg(feature = "dtype-categorical")]
536 (phys, D::Enum(fcats, _)) if &fcats.physical().dtype() == phys => {
537 with_match_categorical_physical_type!(fcats.physical(), |$C| {
538 type CA = ChunkedArray<<$C as PolarsCategoricalType>::PolarsPhysical>;
539 let ca = self.as_ref().as_any().downcast_ref::<CA>().unwrap();
540 Ok(CategoricalChunked::<$C>::from_cats_and_dtype_unchecked(
541 ca.clone(),
542 dtype.clone(),
543 )
544 .into_series())
545 })
546 },
547
548 (D::Int32, D::Date) => feature_gated!("dtype-time", Ok(self.clone().into_date())),
549 (D::Int64, D::Datetime(tu, tz)) => feature_gated!(
550 "dtype-datetime",
551 Ok(self.clone().into_datetime(*tu, tz.clone()))
552 ),
553 (D::Int64, D::Duration(tu)) => {
554 feature_gated!("dtype-duration", Ok(self.clone().into_duration(*tu)))
555 },
556 (D::Int64, D::Time) => feature_gated!("dtype-time", Ok(self.clone().into_time())),
557
558 (D::List(_), D::List(to)) => unsafe {
559 self.list()
560 .unwrap()
561 .from_physical_unchecked(to.as_ref().clone())
562 .map(|ca| ca.into_series())
563 },
564 #[cfg(feature = "dtype-array")]
565 (D::Array(_, lw), D::Array(to, rw)) if lw == rw => unsafe {
566 self.array()
567 .unwrap()
568 .from_physical_unchecked(to.as_ref().clone())
569 .map(|ca| ca.into_series())
570 },
571 #[cfg(feature = "dtype-struct")]
572 (D::Struct(_), D::Struct(to)) => unsafe {
573 self.struct_()
574 .unwrap()
575 .from_physical_unchecked(to.as_slice())
576 .map(|ca| ca.into_series())
577 },
578
579 #[cfg(feature = "dtype-extension")]
580 (_, D::Extension(typ, storage)) => {
581 let storage_series = self.from_physical_unchecked(storage.as_ref())?;
582 let ext = ExtensionChunked::from_storage(typ.clone(), storage_series);
583 Ok(ext.into_series())
584 },
585
586 _ => panic!("invalid from_physical({dtype:?}) for {:?}", self.dtype()),
587 }
588 }
589
590 #[cfg(feature = "dtype-extension")]
591 pub fn into_extension(self, typ: ExtensionTypeInstance) -> Series {
592 assert!(!self.dtype().is_extension());
593 let ext = ExtensionChunked::from_storage(typ, self);
594 ext.into_series()
595 }
596
597 pub fn to_float(&self) -> PolarsResult<Series> {
599 match self.dtype() {
600 DataType::Float32 | DataType::Float64 => Ok(self.clone()),
601 _ => self.cast_with_options(&DataType::Float64, CastOptions::Overflowing),
602 }
603 }
604
605 pub fn sum<T>(&self) -> PolarsResult<T>
612 where
613 T: NumCast + IsFloat,
614 {
615 let sum = self.sum_reduce()?;
616 let sum = sum.value().extract().unwrap();
617 Ok(sum)
618 }
619
620 pub fn min<T>(&self) -> PolarsResult<Option<T>>
623 where
624 T: NumCast + IsFloat,
625 {
626 let min = self.min_reduce()?;
627 let min = min.value().extract::<T>();
628 Ok(min)
629 }
630
631 pub fn max<T>(&self) -> PolarsResult<Option<T>>
634 where
635 T: NumCast + IsFloat,
636 {
637 let max = self.max_reduce()?;
638 let max = max.value().extract::<T>();
639 Ok(max)
640 }
641
642 pub fn explode(&self, options: ExplodeOptions) -> PolarsResult<Series> {
644 match self.dtype() {
645 DataType::List(_) => self.list().unwrap().explode(options),
646 #[cfg(feature = "dtype-array")]
647 DataType::Array(_, _) => self.array().unwrap().explode(options),
648 _ => Ok(self.clone()),
649 }
650 }
651
652 pub fn is_nan(&self) -> PolarsResult<BooleanChunked> {
654 match self.dtype() {
655 #[cfg(feature = "dtype-f16")]
656 DataType::Float16 => Ok(self.f16().unwrap().is_nan()),
657 DataType::Float32 => Ok(self.f32().unwrap().is_nan()),
658 DataType::Float64 => Ok(self.f64().unwrap().is_nan()),
659 DataType::Null => Ok(BooleanChunked::full_null(self.name().clone(), self.len())),
660 dt if dt.is_primitive_numeric() => {
661 let arr = BooleanArray::full(self.len(), false, ArrowDataType::Boolean)
662 .with_validity(self.rechunk_validity());
663 Ok(BooleanChunked::with_chunk(self.name().clone(), arr))
664 },
665 _ => polars_bail!(opq = is_nan, self.dtype()),
666 }
667 }
668
669 pub fn is_not_nan(&self) -> PolarsResult<BooleanChunked> {
671 match self.dtype() {
672 #[cfg(feature = "dtype-f16")]
673 DataType::Float16 => Ok(self.f16().unwrap().is_not_nan()),
674 DataType::Float32 => Ok(self.f32().unwrap().is_not_nan()),
675 DataType::Float64 => Ok(self.f64().unwrap().is_not_nan()),
676 dt if dt.is_primitive_numeric() => {
677 let arr = BooleanArray::full(self.len(), true, ArrowDataType::Boolean)
678 .with_validity(self.rechunk_validity());
679 Ok(BooleanChunked::with_chunk(self.name().clone(), arr))
680 },
681 _ => polars_bail!(opq = is_not_nan, self.dtype()),
682 }
683 }
684
685 pub fn is_finite(&self) -> PolarsResult<BooleanChunked> {
687 match self.dtype() {
688 #[cfg(feature = "dtype-f16")]
689 DataType::Float16 => Ok(self.f16().unwrap().is_finite()),
690 DataType::Float32 => Ok(self.f32().unwrap().is_finite()),
691 DataType::Float64 => Ok(self.f64().unwrap().is_finite()),
692 DataType::Null => Ok(BooleanChunked::full_null(self.name().clone(), self.len())),
693 dt if dt.is_primitive_numeric() => {
694 let arr = BooleanArray::full(self.len(), true, ArrowDataType::Boolean)
695 .with_validity(self.rechunk_validity());
696 Ok(BooleanChunked::with_chunk(self.name().clone(), arr))
697 },
698 _ => polars_bail!(opq = is_finite, self.dtype()),
699 }
700 }
701
702 pub fn is_infinite(&self) -> PolarsResult<BooleanChunked> {
704 match self.dtype() {
705 #[cfg(feature = "dtype-f16")]
706 DataType::Float16 => Ok(self.f16().unwrap().is_infinite()),
707 DataType::Float32 => Ok(self.f32().unwrap().is_infinite()),
708 DataType::Float64 => Ok(self.f64().unwrap().is_infinite()),
709 DataType::Null => Ok(BooleanChunked::full_null(self.name().clone(), self.len())),
710 dt if dt.is_primitive_numeric() => {
711 let arr = BooleanArray::full(self.len(), false, ArrowDataType::Boolean)
712 .with_validity(self.rechunk_validity());
713 Ok(BooleanChunked::with_chunk(self.name().clone(), arr))
714 },
715 _ => polars_bail!(opq = is_infinite, self.dtype()),
716 }
717 }
718
719 #[cfg(feature = "zip_with")]
723 pub fn zip_with(&self, mask: &BooleanChunked, other: &Series) -> PolarsResult<Series> {
724 let (lhs, rhs) = coerce_lhs_rhs(self, other)?;
725 lhs.zip_with_same_type(mask, rhs.as_ref())
726 }
727
728 pub fn to_physical_repr(&self) -> Cow<'_, Series> {
742 use DataType::*;
743 match self.dtype() {
744 #[cfg(feature = "dtype-date")]
747 Date => Cow::Owned(self.date().unwrap().phys.clone().into_series()),
748 #[cfg(feature = "dtype-datetime")]
749 Datetime(_, _) => Cow::Owned(self.datetime().unwrap().phys.clone().into_series()),
750 #[cfg(feature = "dtype-duration")]
751 Duration(_) => Cow::Owned(self.duration().unwrap().phys.clone().into_series()),
752 #[cfg(feature = "dtype-time")]
753 Time => Cow::Owned(self.time().unwrap().phys.clone().into_series()),
754 #[cfg(feature = "dtype-categorical")]
755 dt @ (Categorical(_, _) | Enum(_, _)) => {
756 with_match_categorical_physical_type!(dt.cat_physical().unwrap(), |$C| {
757 let ca = self.cat::<$C>().unwrap();
758 Cow::Owned(ca.physical().clone().into_series())
759 })
760 },
761 #[cfg(feature = "dtype-decimal")]
762 Decimal(_, _) => Cow::Owned(self.decimal().unwrap().phys.clone().into_series()),
763 List(_) => match self.list().unwrap().to_physical_repr() {
764 Cow::Borrowed(_) => Cow::Borrowed(self),
765 Cow::Owned(ca) => Cow::Owned(ca.into_series()),
766 },
767 #[cfg(feature = "dtype-array")]
768 Array(_, _) => match self.array().unwrap().to_physical_repr() {
769 Cow::Borrowed(_) => Cow::Borrowed(self),
770 Cow::Owned(ca) => Cow::Owned(ca.into_series()),
771 },
772 #[cfg(feature = "dtype-struct")]
773 Struct(_) => match self.struct_().unwrap().to_physical_repr() {
774 Cow::Borrowed(_) => Cow::Borrowed(self),
775 Cow::Owned(ca) => Cow::Owned(ca.into_series()),
776 },
777 #[cfg(feature = "dtype-extension")]
778 Extension(_, _) => self.ext().unwrap().storage().to_physical_repr(),
779 _ => Cow::Borrowed(self),
780 }
781 }
782
783 pub fn to_storage(&self) -> &Series {
786 #[cfg(feature = "dtype-extension")]
787 {
788 if let DataType::Extension(_, _) = self.dtype() {
789 return self.ext().unwrap().storage();
790 }
791 }
792 self
793 }
794
795 pub fn gather_every(&self, n: usize, offset: usize) -> PolarsResult<Series> {
797 polars_ensure!(n > 0, ComputeError: "cannot perform gather every for `n=0`");
798 let idx = ((offset as IdxSize)..self.len() as IdxSize)
799 .step_by(n)
800 .collect_ca(PlSmallStr::EMPTY);
801 Ok(unsafe { self.take_unchecked(&idx) })
803 }
804
805 #[cfg(feature = "dot_product")]
806 pub fn dot(&self, other: &Series) -> PolarsResult<f64> {
807 std::ops::Mul::mul(self, other)?.sum::<f64>()
808 }
809
810 pub fn sum_reduce(&self) -> PolarsResult<Scalar> {
816 use DataType::*;
817 match self.dtype() {
818 Int8 | UInt8 | Int16 | UInt16 => self.cast(&Int64).unwrap().sum_reduce(),
819 _ => self.0.sum_reduce(),
820 }
821 }
822
823 pub fn mean_reduce(&self) -> PolarsResult<Scalar> {
826 self.0.mean_reduce()
827 }
828
829 pub fn product(&self) -> PolarsResult<Scalar> {
834 #[cfg(feature = "product")]
835 {
836 use DataType::*;
837 match self.dtype() {
838 Boolean => self.cast(&DataType::Int64).unwrap().product(),
839 Int8 | UInt8 | Int16 | UInt16 | Int32 | UInt32 => {
840 let s = self.cast(&Int64).unwrap();
841 s.product()
842 },
843 Int64 => Ok(self.i64().unwrap().prod_reduce()),
844 UInt64 => Ok(self.u64().unwrap().prod_reduce()),
845 #[cfg(feature = "dtype-i128")]
846 Int128 => Ok(self.i128().unwrap().prod_reduce()),
847 #[cfg(feature = "dtype-u128")]
848 UInt128 => Ok(self.u128().unwrap().prod_reduce()),
849 #[cfg(feature = "dtype-f16")]
850 Float16 => Ok(self.f16().unwrap().prod_reduce()),
851 Float32 => Ok(self.f32().unwrap().prod_reduce()),
852 Float64 => Ok(self.f64().unwrap().prod_reduce()),
853 dt => {
854 polars_bail!(InvalidOperation: "`product` operation not supported for dtype `{dt}`")
855 },
856 }
857 }
858 #[cfg(not(feature = "product"))]
859 {
860 panic!("activate 'product' feature")
861 }
862 }
863
864 pub fn strict_cast(&self, dtype: &DataType) -> PolarsResult<Series> {
866 self.cast_with_options(dtype, CastOptions::Strict)
867 }
868
869 #[cfg(feature = "dtype-decimal")]
870 pub fn into_decimal(self, precision: usize, scale: usize) -> PolarsResult<Series> {
871 match self.dtype() {
872 DataType::Int128 => Ok(self
873 .i128()
874 .unwrap()
875 .clone()
876 .into_decimal(precision, scale)?
877 .into_series()),
878 DataType::Decimal(cur_prec, cur_scale)
879 if scale == *cur_scale && precision >= *cur_prec =>
880 {
881 Ok(self)
882 },
883 dt => panic!("into_decimal({precision:?}, {scale}) not implemented for {dt:?}"),
884 }
885 }
886
887 #[cfg(feature = "dtype-time")]
888 pub fn into_time(self) -> Series {
889 match self.dtype() {
890 DataType::Int64 => self.i64().unwrap().clone().into_time().into_series(),
891 DataType::Time => self
892 .time()
893 .unwrap()
894 .physical()
895 .clone()
896 .into_time()
897 .into_series(),
898 dt => panic!("date not implemented for {dt:?}"),
899 }
900 }
901
902 pub fn into_date(self) -> Series {
903 #[cfg(not(feature = "dtype-date"))]
904 {
905 panic!("activate feature dtype-date")
906 }
907 #[cfg(feature = "dtype-date")]
908 match self.dtype() {
909 DataType::Int32 => self.i32().unwrap().clone().into_date().into_series(),
910 DataType::Date => self
911 .date()
912 .unwrap()
913 .physical()
914 .clone()
915 .into_date()
916 .into_series(),
917 dt => panic!("date not implemented for {dt:?}"),
918 }
919 }
920
921 #[allow(unused_variables)]
922 pub fn into_datetime(self, timeunit: TimeUnit, tz: Option<TimeZone>) -> Series {
923 #[cfg(not(feature = "dtype-datetime"))]
924 {
925 panic!("activate feature dtype-datetime")
926 }
927
928 #[cfg(feature = "dtype-datetime")]
929 match self.dtype() {
930 DataType::Int64 => self
931 .i64()
932 .unwrap()
933 .clone()
934 .into_datetime(timeunit, tz)
935 .into_series(),
936 DataType::Datetime(_, _) => self
937 .datetime()
938 .unwrap()
939 .physical()
940 .clone()
941 .into_datetime(timeunit, tz)
942 .into_series(),
943 dt => panic!("into_datetime not implemented for {dt:?}"),
944 }
945 }
946
947 #[allow(unused_variables)]
948 pub fn into_duration(self, timeunit: TimeUnit) -> Series {
949 #[cfg(not(feature = "dtype-duration"))]
950 {
951 panic!("activate feature dtype-duration")
952 }
953 #[cfg(feature = "dtype-duration")]
954 match self.dtype() {
955 DataType::Int64 => self
956 .i64()
957 .unwrap()
958 .clone()
959 .into_duration(timeunit)
960 .into_series(),
961 DataType::Duration(_) => self
962 .duration()
963 .unwrap()
964 .physical()
965 .clone()
966 .into_duration(timeunit)
967 .into_series(),
968 dt => panic!("into_duration not implemented for {dt:?}"),
969 }
970 }
971
972 pub fn str_value(&self, index: usize) -> PolarsResult<Cow<'_, str>> {
974 Ok(self.0.get(index)?.str_value())
975 }
976 pub fn head(&self, length: Option<usize>) -> Series {
978 let len = length.unwrap_or(HEAD_DEFAULT_LENGTH);
979 self.slice(0, std::cmp::min(len, self.len()))
980 }
981
982 pub fn tail(&self, length: Option<usize>) -> Series {
984 let len = length.unwrap_or(TAIL_DEFAULT_LENGTH);
985 let len = std::cmp::min(len, self.len());
986 self.slice(-(len as i64), len)
987 }
988
989 pub fn unique_stable(&self) -> PolarsResult<Series> {
992 let idx = self.arg_unique()?;
993 unsafe { Ok(self.take_unchecked(&idx)) }
995 }
996
997 pub fn try_idx(&self) -> Option<&IdxCa> {
998 #[cfg(feature = "bigidx")]
999 {
1000 self.try_u64()
1001 }
1002 #[cfg(not(feature = "bigidx"))]
1003 {
1004 self.try_u32()
1005 }
1006 }
1007
1008 pub fn idx(&self) -> PolarsResult<&IdxCa> {
1009 #[cfg(feature = "bigidx")]
1010 {
1011 self.u64()
1012 }
1013 #[cfg(not(feature = "bigidx"))]
1014 {
1015 self.u32()
1016 }
1017 }
1018
1019 pub fn estimated_size(&self) -> usize {
1032 let mut size = 0;
1033 match self.dtype() {
1034 #[cfg(feature = "object")]
1036 DataType::Object(_) => {
1037 let ArrowDataType::FixedSizeBinary(size) = self.chunks()[0].dtype() else {
1038 unreachable!()
1039 };
1040 return self.len() * *size;
1042 },
1043 _ => {},
1044 }
1045
1046 size += self
1047 .chunks()
1048 .iter()
1049 .map(|arr| estimated_bytes_size(&**arr))
1050 .sum::<usize>();
1051
1052 size
1053 }
1054
1055 pub fn as_list(&self) -> ListChunked {
1057 let s = self.rechunk();
1058 let values = s.chunks()[0].clone();
1060 let offsets = (0i64..(s.len() as i64 + 1)).collect::<Vec<_>>();
1061 let offsets = unsafe { Offsets::new_unchecked(offsets) };
1062
1063 let dtype = LargeListArray::default_datatype(
1064 s.dtype().to_physical().to_arrow(CompatLevel::newest()),
1065 );
1066 let new_arr = LargeListArray::new(dtype, offsets.into(), values, None);
1067 let mut out = ListChunked::with_chunk(s.name().clone(), new_arr);
1068 out.set_inner_dtype(s.dtype().clone());
1069 out
1070 }
1071
1072 pub fn row_encode_unordered(&self) -> PolarsResult<BinaryOffsetChunked> {
1073 row_encode::_get_rows_encoded_ca_unordered(
1074 self.name().clone(),
1075 &[self.clone().into_column()],
1076 )
1077 }
1078
1079 pub fn row_encode_ordered(
1080 &self,
1081 descending: bool,
1082 nulls_last: bool,
1083 ) -> PolarsResult<BinaryOffsetChunked> {
1084 row_encode::_get_rows_encoded_ca(
1085 self.name().clone(),
1086 &[self.clone().into_column()],
1087 &[descending],
1088 &[nulls_last],
1089 false,
1090 )
1091 }
1092}
1093
1094impl Default for Series {
1095 fn default() -> Self {
1096 NullChunked::new(PlSmallStr::EMPTY, 0).into_series()
1097 }
1098}
1099
1100impl Deref for Series {
1101 type Target = dyn SeriesTrait;
1102
1103 fn deref(&self) -> &Self::Target {
1104 self.0.as_ref()
1105 }
1106}
1107
1108impl<'a> AsRef<dyn SeriesTrait + 'a> for Series {
1109 fn as_ref(&self) -> &(dyn SeriesTrait + 'a) {
1110 self.0.as_ref()
1111 }
1112}
1113
1114impl<T: PolarsPhysicalType> AsRef<ChunkedArray<T>> for dyn SeriesTrait + '_ {
1115 fn as_ref(&self) -> &ChunkedArray<T> {
1116 let Some(ca) = self.as_any().downcast_ref::<ChunkedArray<T>>() else {
1119 panic!(
1120 "implementation error, cannot get ref {:?} from {:?}",
1121 T::get_static_dtype(),
1122 self.dtype()
1123 );
1124 };
1125
1126 ca
1127 }
1128}
1129
1130impl<T: PolarsPhysicalType> AsMut<ChunkedArray<T>> for dyn SeriesTrait + '_ {
1131 fn as_mut(&mut self) -> &mut ChunkedArray<T> {
1132 if !self.as_any_mut().is::<ChunkedArray<T>>() {
1133 panic!(
1134 "implementation error, cannot get ref {:?} from {:?}",
1135 T::get_static_dtype(),
1136 self.dtype()
1137 );
1138 }
1139
1140 self.as_any_mut().downcast_mut::<ChunkedArray<T>>().unwrap()
1143 }
1144}
1145
1146#[cfg(test)]
1147mod test {
1148 use crate::prelude::*;
1149 use crate::series::*;
1150
1151 #[test]
1152 fn cast() {
1153 let ar = UInt32Chunked::new("a".into(), &[1, 2]);
1154 let s = ar.into_series();
1155 let s2 = s.cast(&DataType::Int64).unwrap();
1156
1157 assert!(s2.i64().is_ok());
1158 let s2 = s.cast(&DataType::Float32).unwrap();
1159 assert!(s2.f32().is_ok());
1160 }
1161
1162 #[test]
1163 fn new_series() {
1164 let _ = Series::new("boolean series".into(), &vec![true, false, true]);
1165 let _ = Series::new("int series".into(), &[1, 2, 3]);
1166 let ca = Int32Chunked::new("a".into(), &[1, 2, 3]);
1167 let _ = ca.into_series();
1168 }
1169
1170 #[test]
1171 #[cfg(feature = "dtype-date")]
1172 fn roundtrip_list_logical_20311() {
1173 let list = ListChunked::from_chunk_iter(
1174 PlSmallStr::from_static("a"),
1175 [ListArray::new(
1176 ArrowDataType::LargeList(Box::new(ArrowField::new(
1177 LIST_VALUES_NAME,
1178 ArrowDataType::Int32,
1179 true,
1180 ))),
1181 unsafe { Offsets::new_unchecked(vec![0, 1]) }.into(),
1182 PrimitiveArray::new(ArrowDataType::Int32, vec![1i32].into(), None).to_boxed(),
1183 None,
1184 )],
1185 );
1186 let list = unsafe { list.from_physical_unchecked(DataType::Date) }.unwrap();
1187 assert_eq!(list.dtype(), &DataType::List(Box::new(DataType::Date)));
1188 }
1189
1190 #[test]
1191 #[cfg(feature = "dtype-struct")]
1192 fn new_series_from_empty_structs() {
1193 let dtype = DataType::Struct(vec![]);
1194 let empties = vec![AnyValue::StructOwned(Box::new((vec![], vec![]))); 3];
1195 let s = Series::from_any_values_and_dtype("".into(), &empties, &dtype, false).unwrap();
1196 assert_eq!(s.len(), 3);
1197 }
1198 #[test]
1199 fn new_series_from_arrow_primitive_array() {
1200 let array = UInt32Array::from_slice([1, 2, 3, 4, 5]);
1201 let array_ref: ArrayRef = Box::new(array);
1202
1203 let _ = Series::try_new("foo".into(), array_ref).unwrap();
1204 }
1205
1206 #[test]
1207 fn series_append() {
1208 let mut s1 = Series::new("a".into(), &[1, 2]);
1209 let s2 = Series::new("b".into(), &[3]);
1210 s1.append(&s2).unwrap();
1211 assert_eq!(s1.len(), 3);
1212
1213 let s2 = Series::new("b".into(), &[3.0]);
1215 assert!(s1.append(&s2).is_err())
1216 }
1217
1218 #[test]
1219 #[cfg(feature = "dtype-decimal")]
1220 fn series_append_decimal() {
1221 let s1 = Series::new("a".into(), &[1.1, 2.3])
1222 .cast(&DataType::Decimal(38, 2))
1223 .unwrap();
1224 let s2 = Series::new("b".into(), &[3])
1225 .cast(&DataType::Decimal(38, 0))
1226 .unwrap();
1227
1228 {
1229 let mut s1 = s1.clone();
1230 s1.append(&s2).unwrap();
1231 assert_eq!(s1.len(), 3);
1232 assert_eq!(s1.get(2).unwrap(), AnyValue::Decimal(300, 38, 2));
1233 }
1234
1235 {
1236 let mut s2 = s2;
1237 s2.extend(&s1).unwrap();
1238 assert_eq!(s2.get(2).unwrap(), AnyValue::Decimal(2, 38, 0));
1239 }
1240 }
1241
1242 #[test]
1243 fn series_slice_works() {
1244 let series = Series::new("a".into(), &[1i64, 2, 3, 4, 5]);
1245
1246 let slice_1 = series.slice(-3, 3);
1247 let slice_2 = series.slice(-5, 5);
1248 let slice_3 = series.slice(0, 5);
1249
1250 assert_eq!(slice_1.get(0).unwrap(), AnyValue::Int64(3));
1251 assert_eq!(slice_2.get(0).unwrap(), AnyValue::Int64(1));
1252 assert_eq!(slice_3.get(0).unwrap(), AnyValue::Int64(1));
1253 }
1254
1255 #[test]
1256 fn out_of_range_slice_does_not_panic() {
1257 let series = Series::new("a".into(), &[1i64, 2, 3, 4, 5]);
1258
1259 let _ = series.slice(-3, 4);
1260 let _ = series.slice(-6, 2);
1261 let _ = series.slice(4, 2);
1262 }
1263}