1use std::borrow::Cow;
2
3use num_traits::{Num, NumCast};
4use polars_arrow::bitmap::{Bitmap, BitmapBuilder};
5use polars_arrow::trusted_len::TrustMyLength;
6use polars_compute::rolling::QuantileMethod;
7use polars_error::{PolarsContext, PolarsResult};
8use polars_utils::aliases::PlSeedableRandomStateQuality;
9use polars_utils::broadcast::{BroadcastLength, broadcast_len};
10use polars_utils::index::check_bounds;
11use polars_utils::pl_str::PlSmallStr;
12pub use scalar::ScalarColumn;
13
14use self::compare_inner::TotalOrdInner;
15use self::gather::check_bounds_ca;
16use self::series::SeriesColumn;
17use crate::chunked_array::cast::CastOptions;
18use crate::chunked_array::flags::StatisticsFlags;
19use crate::datatypes::ReshapeDimension;
20use crate::prelude::*;
21use crate::series::{BitRepr, IsSorted};
22use crate::utils::{Container, slice_offsets};
23use crate::{HEAD_DEFAULT_LENGTH, TAIL_DEFAULT_LENGTH};
24
25mod arithmetic;
26mod compare;
27mod scalar;
28mod series;
29
30#[derive(Debug, Clone)]
40#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
41#[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]
42pub enum Column {
43 Series(SeriesColumn),
44 Scalar(ScalarColumn),
45}
46
47pub trait IntoColumn: Sized {
49 fn into_column(self) -> Column;
50}
51
52impl Column {
53 #[inline]
54 #[track_caller]
55 pub fn new<T, Phantom>(name: PlSmallStr, values: T) -> Self
56 where
57 Phantom: ?Sized,
58 Series: NamedFrom<T, Phantom>,
59 {
60 Self::Series(SeriesColumn::new(NamedFrom::new(name, values)))
61 }
62
63 #[inline]
64 pub fn new_empty(name: PlSmallStr, dtype: &DataType) -> Self {
65 Self::new_scalar(name, Scalar::new(dtype.clone(), AnyValue::Null), 0)
66 }
67
68 #[inline]
69 pub fn new_scalar(name: PlSmallStr, scalar: Scalar, length: usize) -> Self {
70 Self::Scalar(ScalarColumn::new(name, scalar, length))
71 }
72
73 pub fn new_row_index(name: PlSmallStr, offset: IdxSize, length: usize) -> PolarsResult<Column> {
74 let Ok(length) = IdxSize::try_from(length) else {
75 polars_bail!(
76 ComputeError:
77 "row index length {} overflows IdxSize::MAX ({})",
78 length,
79 IdxSize::MAX,
80 )
81 };
82
83 if offset.checked_add(length).is_none() {
84 polars_bail!(
85 ComputeError:
86 "row index with offset {} overflows on dataframe with height {}",
87 offset, length
88 )
89 }
90
91 let range = offset..offset + length;
92
93 let mut ca = IdxCa::from_vec(name, range.collect());
94 ca.set_sorted_flag(IsSorted::Ascending);
95 let col = ca.into_series().into();
96
97 Ok(col)
98 }
99
100 #[inline]
105 pub fn as_materialized_series(&self) -> &Series {
106 match self {
107 Column::Series(s) => s,
108 Column::Scalar(s) => s.as_materialized_series(),
109 }
110 }
111
112 #[inline]
115 pub fn as_materialized_series_maintain_scalar(&self) -> Series {
116 match self {
117 Column::Scalar(s) => s.as_single_value_series(),
118 v => v.as_materialized_series().clone(),
119 }
120 }
121
122 pub fn _get_backing_series(&self) -> Series {
132 match self {
133 Column::Series(s) => (**s).clone(),
134 Column::Scalar(s) => s.as_single_value_series(),
135 }
136 }
137
138 pub fn _to_new_from_backing(&self, new_s: Series) -> Self {
148 match self {
149 Column::Series(s) => {
150 assert_eq!(new_s.len(), s.len());
151 Column::Series(SeriesColumn::new(new_s))
152 },
153 Column::Scalar(s) => {
154 assert_eq!(new_s.len(), s.as_single_value_series().len());
155 Column::Scalar(ScalarColumn::from_single_value_series(new_s, self.len()))
156 },
157 }
158 }
159
160 #[inline]
164 pub fn into_materialized_series(&mut self) -> &mut Series {
165 match self {
166 Column::Series(s) => s,
167 Column::Scalar(s) => {
168 let series = std::mem::replace(
169 s,
170 ScalarColumn::new_empty(PlSmallStr::EMPTY, DataType::Null),
171 )
172 .take_materialized_series();
173 *self = Column::Series(series.into());
174 let Column::Series(s) = self else {
175 unreachable!();
176 };
177 s
178 },
179 }
180 }
181 #[inline]
185 pub fn take_materialized_series(self) -> Series {
186 match self {
187 Column::Series(s) => s.take(),
188 Column::Scalar(s) => s.take_materialized_series(),
189 }
190 }
191
192 #[inline]
193 pub fn dtype(&self) -> &DataType {
194 match self {
195 Column::Series(s) => s.dtype(),
196 Column::Scalar(s) => s.dtype(),
197 }
198 }
199
200 #[inline]
201 pub fn field(&self) -> Cow<'_, Field> {
202 match self {
203 Column::Series(s) => s.field(),
204 Column::Scalar(s) => match s.lazy_as_materialized_series() {
205 None => Cow::Owned(Field::new(s.name().clone(), s.dtype().clone())),
206 Some(s) => s.field(),
207 },
208 }
209 }
210
211 #[inline]
212 pub fn name(&self) -> &PlSmallStr {
213 match self {
214 Column::Series(s) => s.name(),
215 Column::Scalar(s) => s.name(),
216 }
217 }
218
219 #[inline]
220 pub fn len(&self) -> usize {
221 match self {
222 Column::Series(s) => s.len(),
223 Column::Scalar(s) => s.len(),
224 }
225 }
226
227 #[inline]
228 pub fn with_name(mut self, name: PlSmallStr) -> Column {
229 self.rename(name);
230 self
231 }
232
233 #[inline]
234 pub fn rename(&mut self, name: PlSmallStr) {
235 match self {
236 Column::Series(s) => _ = s.rename(name),
237 Column::Scalar(s) => _ = s.rename(name),
238 }
239 }
240
241 #[inline]
243 pub fn as_series(&self) -> Option<&Series> {
244 match self {
245 Column::Series(s) => Some(s),
246 _ => None,
247 }
248 }
249
250 #[inline]
251 pub fn as_scalar_column(&self) -> Option<&ScalarColumn> {
252 match self {
253 Column::Scalar(s) => Some(s),
254 _ => None,
255 }
256 }
257 #[inline]
258 pub fn as_scalar_column_mut(&mut self) -> Option<&mut ScalarColumn> {
259 match self {
260 Column::Scalar(s) => Some(s),
261 _ => None,
262 }
263 }
264
265 pub fn try_bool(&self) -> Option<&BooleanChunked> {
267 self.as_materialized_series().try_bool()
268 }
269 pub fn try_i8(&self) -> Option<&Int8Chunked> {
270 self.as_materialized_series().try_i8()
271 }
272 pub fn try_i16(&self) -> Option<&Int16Chunked> {
273 self.as_materialized_series().try_i16()
274 }
275 pub fn try_i32(&self) -> Option<&Int32Chunked> {
276 self.as_materialized_series().try_i32()
277 }
278 pub fn try_i64(&self) -> Option<&Int64Chunked> {
279 self.as_materialized_series().try_i64()
280 }
281 pub fn try_u8(&self) -> Option<&UInt8Chunked> {
282 self.as_materialized_series().try_u8()
283 }
284 pub fn try_u16(&self) -> Option<&UInt16Chunked> {
285 self.as_materialized_series().try_u16()
286 }
287 pub fn try_u32(&self) -> Option<&UInt32Chunked> {
288 self.as_materialized_series().try_u32()
289 }
290 pub fn try_u64(&self) -> Option<&UInt64Chunked> {
291 self.as_materialized_series().try_u64()
292 }
293 #[cfg(feature = "dtype-u128")]
294 pub fn try_u128(&self) -> Option<&UInt128Chunked> {
295 self.as_materialized_series().try_u128()
296 }
297 #[cfg(feature = "dtype-f16")]
298 pub fn try_f16(&self) -> Option<&Float16Chunked> {
299 self.as_materialized_series().try_f16()
300 }
301 pub fn try_f32(&self) -> Option<&Float32Chunked> {
302 self.as_materialized_series().try_f32()
303 }
304 pub fn try_f64(&self) -> Option<&Float64Chunked> {
305 self.as_materialized_series().try_f64()
306 }
307 pub fn try_str(&self) -> Option<&StringChunked> {
308 self.as_materialized_series().try_str()
309 }
310 pub fn try_list(&self) -> Option<&ListChunked> {
311 self.as_materialized_series().try_list()
312 }
313 pub fn try_binary(&self) -> Option<&BinaryChunked> {
314 self.as_materialized_series().try_binary()
315 }
316 pub fn try_idx(&self) -> Option<&IdxCa> {
317 self.as_materialized_series().try_idx()
318 }
319 pub fn try_binary_offset(&self) -> Option<&BinaryOffsetChunked> {
320 self.as_materialized_series().try_binary_offset()
321 }
322 #[cfg(feature = "dtype-datetime")]
323 pub fn try_datetime(&self) -> Option<&DatetimeChunked> {
324 self.as_materialized_series().try_datetime()
325 }
326 #[cfg(feature = "dtype-struct")]
327 pub fn try_struct(&self) -> Option<&StructChunked> {
328 self.as_materialized_series().try_struct()
329 }
330 #[cfg(feature = "dtype-decimal")]
331 pub fn try_decimal(&self) -> Option<&DecimalChunked> {
332 self.as_materialized_series().try_decimal()
333 }
334 #[cfg(feature = "dtype-array")]
335 pub fn try_array(&self) -> Option<&ArrayChunked> {
336 self.as_materialized_series().try_array()
337 }
338 #[cfg(feature = "dtype-categorical")]
339 pub fn try_cat<T: PolarsCategoricalType>(&self) -> Option<&CategoricalChunked<T>> {
340 self.as_materialized_series().try_cat::<T>()
341 }
342 #[cfg(feature = "dtype-categorical")]
343 pub fn try_cat8(&self) -> Option<&Categorical8Chunked> {
344 self.as_materialized_series().try_cat8()
345 }
346 #[cfg(feature = "dtype-categorical")]
347 pub fn try_cat16(&self) -> Option<&Categorical16Chunked> {
348 self.as_materialized_series().try_cat16()
349 }
350 #[cfg(feature = "dtype-categorical")]
351 pub fn try_cat32(&self) -> Option<&Categorical32Chunked> {
352 self.as_materialized_series().try_cat32()
353 }
354 #[cfg(feature = "dtype-date")]
355 pub fn try_date(&self) -> Option<&DateChunked> {
356 self.as_materialized_series().try_date()
357 }
358 #[cfg(feature = "dtype-duration")]
359 pub fn try_duration(&self) -> Option<&DurationChunked> {
360 self.as_materialized_series().try_duration()
361 }
362
363 pub fn bool(&self) -> PolarsResult<&BooleanChunked> {
365 self.as_materialized_series().bool()
366 }
367 pub fn i8(&self) -> PolarsResult<&Int8Chunked> {
368 self.as_materialized_series().i8()
369 }
370 pub fn i16(&self) -> PolarsResult<&Int16Chunked> {
371 self.as_materialized_series().i16()
372 }
373 pub fn i32(&self) -> PolarsResult<&Int32Chunked> {
374 self.as_materialized_series().i32()
375 }
376 pub fn i64(&self) -> PolarsResult<&Int64Chunked> {
377 self.as_materialized_series().i64()
378 }
379 #[cfg(feature = "dtype-i128")]
380 pub fn i128(&self) -> PolarsResult<&Int128Chunked> {
381 self.as_materialized_series().i128()
382 }
383 pub fn u8(&self) -> PolarsResult<&UInt8Chunked> {
384 self.as_materialized_series().u8()
385 }
386 pub fn u16(&self) -> PolarsResult<&UInt16Chunked> {
387 self.as_materialized_series().u16()
388 }
389 pub fn u32(&self) -> PolarsResult<&UInt32Chunked> {
390 self.as_materialized_series().u32()
391 }
392 pub fn u64(&self) -> PolarsResult<&UInt64Chunked> {
393 self.as_materialized_series().u64()
394 }
395 #[cfg(feature = "dtype-u128")]
396 pub fn u128(&self) -> PolarsResult<&UInt128Chunked> {
397 self.as_materialized_series().u128()
398 }
399 #[cfg(feature = "dtype-f16")]
400 pub fn f16(&self) -> PolarsResult<&Float16Chunked> {
401 self.as_materialized_series().f16()
402 }
403 pub fn f32(&self) -> PolarsResult<&Float32Chunked> {
404 self.as_materialized_series().f32()
405 }
406 pub fn f64(&self) -> PolarsResult<&Float64Chunked> {
407 self.as_materialized_series().f64()
408 }
409 pub fn str(&self) -> PolarsResult<&StringChunked> {
410 self.as_materialized_series().str()
411 }
412 pub fn list(&self) -> PolarsResult<&ListChunked> {
413 self.as_materialized_series().list()
414 }
415 pub fn binary(&self) -> PolarsResult<&BinaryChunked> {
416 self.as_materialized_series().binary()
417 }
418 pub fn idx(&self) -> PolarsResult<&IdxCa> {
419 self.as_materialized_series().idx()
420 }
421 pub fn binary_offset(&self) -> PolarsResult<&BinaryOffsetChunked> {
422 self.as_materialized_series().binary_offset()
423 }
424 #[cfg(feature = "dtype-datetime")]
425 pub fn datetime(&self) -> PolarsResult<&DatetimeChunked> {
426 self.as_materialized_series().datetime()
427 }
428 #[cfg(feature = "dtype-struct")]
429 pub fn struct_(&self) -> PolarsResult<&StructChunked> {
430 self.as_materialized_series().struct_()
431 }
432 #[cfg(feature = "dtype-decimal")]
433 pub fn decimal(&self) -> PolarsResult<&DecimalChunked> {
434 self.as_materialized_series().decimal()
435 }
436 #[cfg(feature = "dtype-array")]
437 pub fn array(&self) -> PolarsResult<&ArrayChunked> {
438 self.as_materialized_series().array()
439 }
440 #[cfg(feature = "dtype-categorical")]
441 pub fn cat<T: PolarsCategoricalType>(&self) -> PolarsResult<&CategoricalChunked<T>> {
442 self.as_materialized_series().cat::<T>()
443 }
444 #[cfg(feature = "dtype-categorical")]
445 pub fn cat8(&self) -> PolarsResult<&Categorical8Chunked> {
446 self.as_materialized_series().cat8()
447 }
448 #[cfg(feature = "dtype-categorical")]
449 pub fn cat16(&self) -> PolarsResult<&Categorical16Chunked> {
450 self.as_materialized_series().cat16()
451 }
452 #[cfg(feature = "dtype-categorical")]
453 pub fn cat32(&self) -> PolarsResult<&Categorical32Chunked> {
454 self.as_materialized_series().cat32()
455 }
456 #[cfg(feature = "dtype-date")]
457 pub fn date(&self) -> PolarsResult<&DateChunked> {
458 self.as_materialized_series().date()
459 }
460 #[cfg(feature = "dtype-duration")]
461 pub fn duration(&self) -> PolarsResult<&DurationChunked> {
462 self.as_materialized_series().duration()
463 }
464
465 pub fn cast_with_options(&self, dtype: &DataType, options: CastOptions) -> PolarsResult<Self> {
467 match self {
468 Column::Series(s) => s.cast_with_options(dtype, options).map(Column::from),
469 Column::Scalar(s) => s.cast_with_options(dtype, options).map(Column::from),
470 }
471 }
472 pub fn strict_cast(&self, dtype: &DataType) -> PolarsResult<Self> {
473 match self {
474 Column::Series(s) => s.strict_cast(dtype).map(Column::from),
475 Column::Scalar(s) => s.strict_cast(dtype).map(Column::from),
476 }
477 }
478 pub fn cast(&self, dtype: &DataType) -> PolarsResult<Column> {
479 match self {
480 Column::Series(s) => s.cast(dtype).map(Column::from),
481 Column::Scalar(s) => s.cast(dtype).map(Column::from),
482 }
483 }
484 pub unsafe fn cast_unchecked(&self, dtype: &DataType) -> PolarsResult<Column> {
488 match self {
489 Column::Series(s) => unsafe { s.cast_unchecked(dtype) }.map(Column::from),
490 Column::Scalar(s) => unsafe { s.cast_unchecked(dtype) }.map(Column::from),
491 }
492 }
493
494 #[must_use]
495 pub fn clear(&self) -> Self {
496 match self {
497 Column::Series(s) => s.clear().into(),
498 Column::Scalar(s) => s.resize(0).into(),
499 }
500 }
501
502 #[inline]
503 pub fn shrink_to_fit(&mut self) {
504 match self {
505 Column::Series(s) => s.shrink_to_fit(),
506 Column::Scalar(_) => {},
507 }
508 }
509
510 #[inline]
511 pub fn new_from_index(&self, index: usize, length: usize) -> Self {
512 if index >= self.len() {
513 return Self::full_null(self.name().clone(), length, self.dtype());
514 }
515
516 match self {
517 Column::Series(s) => {
518 let av = unsafe { s.get_unchecked(index) };
520 let scalar = Scalar::new(self.dtype().clone(), av.into_static());
521 Self::new_scalar(self.name().clone(), scalar, length)
522 },
523 Column::Scalar(s) => s.resize(length).into(),
524 }
525 }
526
527 pub fn broadcast_to(&self, length: usize) -> PolarsResult<Cow<'_, Self>> {
531 let len = self.len();
532 if len == length {
533 Ok(Cow::Borrowed(self))
534 } else if len == 1 {
535 Ok(Cow::Owned(self.new_from_index(0, length)))
536 } else {
537 polars_bail!(
538 ShapeMismatch: "can't broadcast Series '{}' of length {len} to length {length}",
539 self.name()
540 );
541 }
542 }
543
544 pub fn broadcast_in_place_to(&mut self, length: usize) -> PolarsResult<()> {
546 if let Cow::Owned(new) = self.broadcast_to(length)? {
547 *self = new;
548 }
549 Ok(())
550 }
551
552 pub fn broadcast_owned_to(mut self, length: usize) -> PolarsResult<Self> {
554 self.broadcast_in_place_to(length)?;
555 Ok(self)
556 }
557
558 #[inline]
559 pub fn has_nulls(&self) -> bool {
560 match self {
561 Self::Series(s) => s.has_nulls(),
562 Self::Scalar(s) => s.has_nulls(),
563 }
564 }
565
566 #[inline]
567 pub fn is_null(&self) -> BooleanChunked {
568 match self {
569 Self::Series(s) => s.is_null(),
570 Self::Scalar(s) => {
571 BooleanChunked::full(s.name().clone(), s.scalar().is_null(), s.len())
572 },
573 }
574 }
575 #[inline]
576 pub fn is_not_null(&self) -> BooleanChunked {
577 match self {
578 Self::Series(s) => s.is_not_null(),
579 Self::Scalar(s) => {
580 BooleanChunked::full(s.name().clone(), !s.scalar().is_null(), s.len())
581 },
582 }
583 }
584
585 pub fn to_physical_repr(&self) -> Column {
586 self.as_materialized_series()
588 .to_physical_repr()
589 .into_owned()
590 .into()
591 }
592 pub unsafe fn from_physical_unchecked(&self, dtype: &DataType) -> PolarsResult<Column> {
596 self.as_materialized_series()
598 .from_physical_unchecked(dtype)
599 .map(Column::from)
600 }
601
602 pub fn head(&self, length: Option<usize>) -> Column {
603 let len = length.unwrap_or(HEAD_DEFAULT_LENGTH);
604 let len = usize::min(len, self.len());
605 self.slice(0, len)
606 }
607 pub fn tail(&self, length: Option<usize>) -> Column {
608 let len = length.unwrap_or(TAIL_DEFAULT_LENGTH);
609 let len = usize::min(len, self.len());
610 debug_assert!(len <= i64::MAX as usize);
611 self.slice(-(len as i64), len)
612 }
613 pub fn slice(&self, offset: i64, length: usize) -> Column {
614 match self {
615 Column::Series(s) => s.slice(offset, length).into(),
616 Column::Scalar(s) => {
617 let (_, length) = slice_offsets(offset, length, s.len());
618 s.resize(length).into()
619 },
620 }
621 }
622
623 pub fn split_at(&self, offset: i64) -> (Column, Column) {
624 match self {
625 Column::Scalar(c) => {
626 let len = c.len();
627 let offset = if offset < 0 {
628 let offset_abs = usize::try_from(offset.strict_abs())
629 .expect("offset exceeds usize limits")
630 .min(len);
631 len - offset_abs
632 } else {
633 usize::try_from(offset)
634 .expect("offset exceeds usize limits")
635 .min(len)
636 };
637 (
638 Column::Scalar(c.resize(offset)),
639 Column::Scalar(c.resize(len - offset)),
640 )
641 },
642 Column::Series(_) => {
643 let (l, r) = self.as_materialized_series().split_at(offset);
644 (l.into(), r.into())
645 },
646 }
647 }
648
649 #[inline]
650 pub fn null_count(&self) -> usize {
651 match self {
652 Self::Series(s) => s.null_count(),
653 Self::Scalar(s) if s.scalar().is_null() => s.len(),
654 Self::Scalar(_) => 0,
655 }
656 }
657
658 pub fn first_non_null(&self) -> Option<usize> {
659 match self {
660 Self::Series(s) => crate::utils::first_non_null(s.chunks().iter().map(|a| a.as_ref())),
661 Self::Scalar(s) => (!s.scalar().is_null() && !s.is_empty()).then_some(0),
662 }
663 }
664
665 pub fn last_non_null(&self) -> Option<usize> {
666 match self {
667 Self::Series(s) => {
668 crate::utils::last_non_null(s.chunks().iter().map(|a| a.as_ref()), s.len())
669 },
670 Self::Scalar(s) => (!s.scalar().is_null() && !s.is_empty()).then(|| s.len() - 1),
671 }
672 }
673
674 pub fn take(&self, indices: &IdxCa) -> PolarsResult<Column> {
675 check_bounds_ca(indices, self.len() as IdxSize)?;
676 Ok(unsafe { self.take_unchecked(indices) })
677 }
678 pub fn take_slice(&self, indices: &[IdxSize]) -> PolarsResult<Column> {
679 check_bounds(indices, self.len() as IdxSize)?;
680 Ok(unsafe { self.take_slice_unchecked(indices) })
681 }
682 pub unsafe fn take_unchecked(&self, indices: &IdxCa) -> Column {
686 debug_assert!(check_bounds_ca(indices, self.len() as IdxSize).is_ok());
687
688 match self {
689 Self::Series(s) => unsafe { s.take_unchecked(indices) }.into(),
690 Self::Scalar(s) => {
691 let idxs_length = indices.len();
692 let idxs_null_count = indices.null_count();
693
694 let scalar = ScalarColumn::from_single_value_series(
695 s.as_single_value_series().take_unchecked(&IdxCa::new(
696 indices.name().clone(),
697 &[0][..s.len().min(1)],
698 )),
699 idxs_length,
700 );
701
702 if idxs_null_count == 0 || scalar.has_nulls() {
704 scalar.into_column()
705 } else if idxs_null_count == idxs_length {
706 scalar.into_nulls().into_column()
707 } else {
708 let validity = indices.rechunk_validity();
709 let mut out = scalar.take_materialized_series().with_validity(validity);
711 out.set_sorted_flag(IsSorted::Not);
713 out.into_column()
714 }
715 },
716 }
717 }
718 pub unsafe fn take_slice_unchecked(&self, indices: &[IdxSize]) -> Column {
722 debug_assert!(check_bounds(indices, self.len() as IdxSize).is_ok());
723
724 match self {
725 Self::Series(s) => unsafe { s.take_slice_unchecked(indices) }.into(),
726 Self::Scalar(s) => ScalarColumn::from_single_value_series(
727 s.as_single_value_series()
728 .take_slice_unchecked(&[0][..s.len().min(1)]),
729 indices.len(),
730 )
731 .into(),
732 }
733 }
734
735 #[inline(always)]
737 #[cfg(any(feature = "algorithm_group_by", feature = "bitwise"))]
738 fn agg_with_scalar_identity(
739 &self,
740 groups: &GroupsType,
741 series_agg: impl Fn(&Series, &GroupsType) -> Series,
742 ) -> Column {
743 match self {
744 Column::Series(s) => series_agg(s, groups).into_column(),
745 Column::Scalar(s) => {
746 if s.is_empty() {
747 return series_agg(s.as_materialized_series(), groups).into_column();
748 }
749
750 let series_aggregation = series_agg(
754 &s.as_single_value_series(),
755 &GroupsType::new_slice(vec![[0, 1]], false, true),
757 );
758
759 if series_aggregation.has_nulls() {
761 return Self::new_scalar(
762 series_aggregation.name().clone(),
763 Scalar::new(series_aggregation.dtype().clone(), AnyValue::Null),
764 groups.len(),
765 );
766 }
767
768 let scalar_col =
771 ScalarColumn::from_single_value_series(series_aggregation, groups.len());
772
773 let Some(first_empty_idx) = groups.iter().position(|g| g.is_empty()) else {
774 return scalar_col.into_column();
776 };
777
778 let mut validity = BitmapBuilder::with_capacity(groups.len());
780 validity.extend_constant(first_empty_idx, true);
781 let iter = unsafe {
783 TrustMyLength::new(
784 groups.iter().skip(first_empty_idx).map(|g| !g.is_empty()),
785 groups.len() - first_empty_idx,
786 )
787 };
788 validity.extend_trusted_len_iter(iter);
789
790 let s = scalar_col.take_materialized_series().rechunk();
792 s.with_validity(validity.into_opt_validity()).into_column()
793 },
794 }
795 }
796
797 #[cfg(feature = "algorithm_group_by")]
801 pub unsafe fn agg_min(&self, groups: &GroupsType) -> Self {
802 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_min(g) })
803 }
804
805 #[cfg(feature = "algorithm_group_by")]
809 pub unsafe fn agg_max(&self, groups: &GroupsType) -> Self {
810 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_max(g) })
811 }
812
813 #[cfg(feature = "algorithm_group_by")]
817 pub unsafe fn agg_mean(&self, groups: &GroupsType) -> Self {
818 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_mean(g) })
819 }
820
821 #[cfg(feature = "algorithm_group_by")]
822 fn scalar_agg_arg_min_max(sc: &ScalarColumn, groups: &GroupsType) -> Self {
823 let name = sc.name().clone();
824 if sc.is_empty() || sc.has_nulls() {
825 return Self::full_null(name, groups.len(), &IDX_DTYPE);
826 }
827
828 if groups.iter().any(|g| g.is_empty()) {
830 return IdxCa::from_iter_options(
831 name,
832 groups.iter().map(|g| (!g.is_empty()).then_some(0)),
833 )
834 .into_column();
835 }
836
837 Self::new_scalar(name, Scalar::new_idxsize(0), groups.len())
838 }
839
840 #[cfg(feature = "algorithm_group_by")]
844 pub unsafe fn agg_arg_min(&self, groups: &GroupsType) -> Self {
845 match self {
846 Column::Series(s) => unsafe { Column::from(s.agg_arg_min(groups)) },
847 Column::Scalar(sc) => Self::scalar_agg_arg_min_max(sc, groups),
848 }
849 }
850
851 #[cfg(feature = "algorithm_group_by")]
855 pub unsafe fn agg_arg_max(&self, groups: &GroupsType) -> Self {
856 match self {
857 Column::Series(s) => unsafe { Column::from(s.agg_arg_max(groups)) },
858 Column::Scalar(sc) => Self::scalar_agg_arg_min_max(sc, groups),
859 }
860 }
861
862 #[cfg(feature = "algorithm_group_by")]
866 pub unsafe fn agg_sum(&self, groups: &GroupsType) -> Self {
867 unsafe { self.as_materialized_series().agg_sum(groups) }.into()
869 }
870
871 #[cfg(feature = "algorithm_group_by")]
875 pub unsafe fn agg_first(&self, groups: &GroupsType) -> Self {
876 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_first(g) })
877 }
878
879 #[cfg(feature = "algorithm_group_by")]
883 pub unsafe fn agg_first_non_null(&self, groups: &GroupsType) -> Self {
884 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_first_non_null(g) })
885 }
886
887 #[cfg(feature = "algorithm_group_by")]
891 pub unsafe fn agg_last(&self, groups: &GroupsType) -> Self {
892 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_last(g) })
893 }
894
895 #[cfg(feature = "algorithm_group_by")]
899 pub unsafe fn agg_last_non_null(&self, groups: &GroupsType) -> Self {
900 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_last_non_null(g) })
901 }
902
903 #[cfg(feature = "algorithm_group_by")]
907 pub unsafe fn agg_n_unique(&self, groups: &GroupsType) -> Self {
908 unsafe { self.as_materialized_series().agg_n_unique(groups) }.into()
910 }
911
912 #[cfg(feature = "algorithm_group_by")]
916 pub unsafe fn agg_quantile(
917 &self,
918 groups: &GroupsType,
919 quantile: f64,
920 method: QuantileMethod,
921 ) -> Self {
922 unsafe {
925 self.as_materialized_series()
926 .agg_quantile(groups, quantile, method)
927 }
928 .into()
929 }
930
931 #[cfg(feature = "algorithm_group_by")]
935 pub unsafe fn agg_median(&self, groups: &GroupsType) -> Self {
936 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_median(g) })
937 }
938
939 #[cfg(feature = "algorithm_group_by")]
943 pub unsafe fn agg_var(&self, groups: &GroupsType, ddof: u8) -> Self {
944 unsafe { self.as_materialized_series().agg_var(groups, ddof) }.into()
946 }
947
948 #[cfg(feature = "algorithm_group_by")]
952 pub unsafe fn agg_std(&self, groups: &GroupsType, ddof: u8) -> Self {
953 unsafe { self.as_materialized_series().agg_std(groups, ddof) }.into()
955 }
956
957 #[cfg(feature = "algorithm_group_by")]
961 pub unsafe fn agg_list(&self, groups: &GroupsType) -> Self {
962 unsafe { self.as_materialized_series().agg_list(groups) }.into()
964 }
965
966 #[cfg(feature = "algorithm_group_by")]
970 pub unsafe fn agg_valid_count(&self, groups: &GroupsType) -> Self {
971 unsafe { self.as_materialized_series().agg_valid_count(groups) }.into()
973 }
974
975 #[cfg(feature = "bitwise")]
979 pub unsafe fn agg_and(&self, groups: &GroupsType) -> Self {
980 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_and(g) })
981 }
982 #[cfg(feature = "bitwise")]
986 pub unsafe fn agg_or(&self, groups: &GroupsType) -> Self {
987 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_or(g) })
988 }
989 #[cfg(feature = "bitwise")]
993 pub unsafe fn agg_xor(&self, groups: &GroupsType) -> Self {
994 unsafe { self.as_materialized_series().agg_xor(groups) }.into()
996 }
997
998 pub fn full_null(name: PlSmallStr, size: usize, dtype: &DataType) -> Self {
999 Self::new_scalar(name, Scalar::new(dtype.clone(), AnyValue::Null), size)
1000 }
1001
1002 pub fn is_empty(&self) -> bool {
1003 self.len() == 0
1004 }
1005
1006 pub fn is_full_null(&self) -> bool {
1007 match self {
1008 Column::Series(s) => s.is_full_null(),
1009 Column::Scalar(s) => s.is_full_null(),
1010 }
1011 }
1012
1013 pub fn reverse(&self) -> Column {
1014 match self {
1015 Column::Series(s) => s.reverse().into(),
1016 Column::Scalar(_) => self.clone(),
1017 }
1018 }
1019
1020 pub fn equals(&self, other: &Column) -> bool {
1021 self.as_materialized_series()
1023 .equals(other.as_materialized_series())
1024 }
1025
1026 pub fn equals_missing(&self, other: &Column) -> bool {
1027 self.as_materialized_series()
1029 .equals_missing(other.as_materialized_series())
1030 }
1031
1032 pub fn set_sorted_flag(&mut self, sorted: IsSorted) {
1033 match self {
1035 Column::Series(s) => s.set_sorted_flag(sorted),
1036 Column::Scalar(_) => {},
1037 }
1038 }
1039
1040 pub fn get_flags(&self) -> StatisticsFlags {
1041 match self {
1042 Column::Series(s) => s.get_flags(),
1043 Column::Scalar(_) => {
1044 StatisticsFlags::IS_SORTED_ASC | StatisticsFlags::CAN_FAST_EXPLODE_LIST
1045 },
1046 }
1047 }
1048
1049 pub fn set_flags(&mut self, flags: StatisticsFlags) -> bool {
1051 match self {
1052 Column::Series(s) => {
1053 s.set_flags(flags);
1054 true
1055 },
1056 Column::Scalar(_) => false,
1057 }
1058 }
1059
1060 pub fn vec_hash(
1061 &self,
1062 build_hasher: PlSeedableRandomStateQuality,
1063 buf: &mut Vec<u64>,
1064 ) -> PolarsResult<()> {
1065 self.as_materialized_series().vec_hash(build_hasher, buf)
1067 }
1068
1069 pub fn vec_hash_combine(
1070 &self,
1071 build_hasher: PlSeedableRandomStateQuality,
1072 hashes: &mut [u64],
1073 ) -> PolarsResult<()> {
1074 self.as_materialized_series()
1076 .vec_hash_combine(build_hasher, hashes)
1077 }
1078
1079 fn try_append_scalar(&mut self, other: &Column) -> bool {
1083 let (Column::Scalar(lhs), Column::Scalar(rhs)) = (&mut *self, other) else {
1084 return false;
1085 };
1086 lhs.try_append(rhs)
1087 }
1088
1089 pub fn append(&mut self, other: &Column) -> PolarsResult<&mut Self> {
1090 if self.try_append_scalar(other) {
1091 return Ok(self);
1092 }
1093
1094 self.into_materialized_series()
1095 .append(other.as_materialized_series())?;
1096 Ok(self)
1097 }
1098 pub fn append_owned(&mut self, other: Column) -> PolarsResult<&mut Self> {
1099 if self.try_append_scalar(&other) {
1100 return Ok(self);
1101 }
1102
1103 self.into_materialized_series()
1104 .append_owned(other.take_materialized_series())?;
1105 Ok(self)
1106 }
1107
1108 pub fn arg_sort(&self, options: SortOptions) -> IdxCa {
1109 if self.is_empty() {
1110 return IdxCa::from_vec(self.name().clone(), Vec::new());
1111 }
1112
1113 if self.null_count() == self.len() {
1114 return IdxCa::from_iter_values(self.name().clone(), 0..self.len() as IdxSize);
1117 }
1118
1119 let is_sorted = Some(self.is_sorted_flag());
1120 let Some(is_sorted) = is_sorted.filter(|v| !matches!(v, IsSorted::Not)) else {
1121 return self.as_materialized_series().arg_sort(options);
1122 };
1123
1124 let is_sorted_dsc = matches!(is_sorted, IsSorted::Descending);
1126 let invert = options.descending != is_sorted_dsc;
1127
1128 let mut values = Vec::with_capacity(self.len());
1129
1130 #[inline(never)]
1131 fn extend(
1132 start: IdxSize,
1133 end: IdxSize,
1134 slf: &Column,
1135 values: &mut Vec<IdxSize>,
1136 is_only_nulls: bool,
1137 invert: bool,
1138 maintain_order: bool,
1139 ) {
1140 debug_assert!(start <= end);
1141 debug_assert!(start as usize <= slf.len());
1142 debug_assert!(end as usize <= slf.len());
1143
1144 if !invert || is_only_nulls {
1145 values.extend(start..end);
1146 return;
1147 }
1148
1149 if !maintain_order {
1151 values.extend((start..end).rev());
1152 return;
1153 }
1154
1155 let arg_unique = slf
1161 .slice(start as i64, (end - start) as usize)
1162 .arg_unique()
1163 .unwrap();
1164
1165 assert!(!arg_unique.has_nulls());
1166
1167 let num_unique = arg_unique.len();
1168
1169 if num_unique == (end - start) as usize {
1171 values.extend((start..end).rev());
1172 return;
1173 }
1174
1175 if num_unique == 1 {
1176 values.extend(start..end);
1177 return;
1178 }
1179
1180 let mut prev_idx = end - start;
1181 for chunk in arg_unique.downcast_iter() {
1182 for &idx in chunk.values().as_slice().iter().rev() {
1183 values.extend(start + idx..start + prev_idx);
1184 prev_idx = idx;
1185 }
1186 }
1187 }
1188 macro_rules! extend {
1189 ($start:expr, $end:expr) => {
1190 extend!($start, $end, is_only_nulls = false);
1191 };
1192 ($start:expr, $end:expr, is_only_nulls = $is_only_nulls:expr) => {
1193 extend(
1194 $start,
1195 $end,
1196 self,
1197 &mut values,
1198 $is_only_nulls,
1199 invert,
1200 options.maintain_order,
1201 );
1202 };
1203 }
1204
1205 let length = self.len() as IdxSize;
1206 let null_count = self.null_count() as IdxSize;
1207
1208 if null_count == 0 {
1209 extend!(0, length);
1210 } else {
1211 let has_nulls_last = self.get(self.len() - 1).unwrap().is_null();
1212 match (options.nulls_last, has_nulls_last) {
1213 (true, true) => {
1214 extend!(0, length - null_count);
1216 extend!(length - null_count, length, is_only_nulls = true);
1217 },
1218 (true, false) => {
1219 extend!(null_count, length);
1221 extend!(0, null_count, is_only_nulls = true);
1222 },
1223 (false, true) => {
1224 extend!(length - null_count, length, is_only_nulls = true);
1226 extend!(0, length - null_count);
1227 },
1228 (false, false) => {
1229 extend!(0, null_count, is_only_nulls = true);
1231 extend!(null_count, length);
1232 },
1233 }
1234 }
1235
1236 if let Some(limit) = options.limit {
1239 let limit = limit.min(length);
1240 values.truncate(limit as usize);
1241 }
1242
1243 IdxCa::from_vec(self.name().clone(), values)
1244 }
1245
1246 pub fn arg_sort_multiple(
1247 &self,
1248 by: &[Column],
1249 options: &SortMultipleOptions,
1250 ) -> PolarsResult<IdxCa> {
1251 self.as_materialized_series().arg_sort_multiple(by, options)
1253 }
1254
1255 pub fn arg_unique(&self) -> PolarsResult<IdxCa> {
1256 match self {
1257 Column::Scalar(s) => Ok(IdxCa::new_vec(s.name().clone(), vec![0])),
1258 _ => self.as_materialized_series().arg_unique(),
1259 }
1260 }
1261
1262 pub fn bit_repr(&self) -> Option<BitRepr> {
1263 self.as_materialized_series().bit_repr()
1265 }
1266
1267 pub fn into_frame(self) -> DataFrame {
1268 unsafe { DataFrame::new_unchecked(self.len(), vec![self]) }
1270 }
1271
1272 pub fn extend(&mut self, other: &Column) -> PolarsResult<&mut Self> {
1273 if self.try_append_scalar(other) {
1276 return Ok(self);
1277 }
1278
1279 self.into_materialized_series()
1280 .extend(other.as_materialized_series())?;
1281 Ok(self)
1282 }
1283
1284 pub fn rechunk(&self) -> Column {
1285 match self {
1286 Column::Series(s) => s.rechunk().into(),
1287 Column::Scalar(s) => {
1288 if s.lazy_as_materialized_series()
1289 .filter(|x| x.n_chunks() > 1)
1290 .is_some()
1291 {
1292 Column::Scalar(ScalarColumn::new(
1293 s.name().clone(),
1294 s.scalar().clone(),
1295 s.len(),
1296 ))
1297 } else {
1298 self.clone()
1299 }
1300 },
1301 }
1302 }
1303
1304 pub fn explode(&self, options: ExplodeOptions) -> PolarsResult<Column> {
1305 self.as_materialized_series()
1306 .explode(options)
1307 .map(Column::from)
1308 }
1309 pub fn implode(&self) -> PolarsResult<ListChunked> {
1310 self.as_materialized_series().implode()
1311 }
1312
1313 pub fn fill_null(&self, strategy: FillNullStrategy) -> PolarsResult<Self> {
1314 self.as_materialized_series()
1316 .fill_null(strategy)
1317 .map(Column::from)
1318 }
1319
1320 pub fn divide(&self, rhs: &Column) -> PolarsResult<Self> {
1321 self.as_materialized_series()
1323 .divide(rhs.as_materialized_series())
1324 .map(Column::from)
1325 }
1326
1327 pub fn shift(&self, periods: i64) -> Column {
1328 self.as_materialized_series().shift(periods).into()
1330 }
1331
1332 pub fn with_validity(&self, validity: Option<Bitmap>) -> Column {
1333 match self {
1334 Column::Series(s) => Column::from(s.with_validity(validity)),
1335 Column::Scalar(s) => match validity {
1336 Some(v) => Column::from(s.as_materialized_series().with_validity(Some(v))),
1337 None => Column::Scalar(s.clone()),
1338 },
1339 }
1340 }
1341
1342 pub fn mask(&self, validity: &Bitmap) -> Column {
1343 if validity.len() == 1 {
1344 if validity.get_bit(0) {
1345 self.clone()
1346 } else {
1347 Self::full_null(self.name().clone(), self.len(), self.dtype())
1348 }
1349 } else {
1350 Column::from(self.as_materialized_series().mask(validity))
1351 }
1352 }
1353
1354 #[cfg(feature = "zip_with")]
1355 pub fn zip_with(&self, mask: &BooleanChunked, other: &Self) -> PolarsResult<Self> {
1356 self.as_materialized_series()
1358 .zip_with(mask, other.as_materialized_series())
1359 .map(Self::from)
1360 }
1361
1362 #[cfg(feature = "zip_with")]
1363 pub fn zip_with_same_type(
1364 &self,
1365 mask: &ChunkedArray<BooleanType>,
1366 other: &Column,
1367 ) -> PolarsResult<Column> {
1368 self.as_materialized_series()
1370 .zip_with_same_type(mask, other.as_materialized_series())
1371 .map(Column::from)
1372 }
1373
1374 pub fn drop_nulls(&self) -> Column {
1375 match self {
1376 Column::Series(s) => s.drop_nulls().into_column(),
1377 Column::Scalar(s) => s.drop_nulls().into_column(),
1378 }
1379 }
1380
1381 pub fn to_unit_list(&self) -> Column {
1383 match self {
1385 Column::Series(s) => s.to_unit_list().into_column(),
1386 Column::Scalar(s) => s.to_unit_list().into_column(),
1387 }
1388 }
1389
1390 pub fn is_sorted_flag(&self) -> IsSorted {
1391 match self {
1392 Column::Series(s) => s.is_sorted_flag(),
1393 Column::Scalar(_) => IsSorted::Ascending,
1394 }
1395 }
1396
1397 pub fn unique(&self) -> PolarsResult<Column> {
1398 match self {
1399 Column::Series(s) => s.unique().map(Column::from),
1400 Column::Scalar(s) => {
1401 _ = s.as_single_value_series().unique()?;
1402 if s.is_empty() {
1403 return Ok(s.clone().into_column());
1404 }
1405
1406 Ok(s.resize(1).into_column())
1407 },
1408 }
1409 }
1410 pub fn unique_stable(&self) -> PolarsResult<Column> {
1411 match self {
1412 Column::Series(s) => s.unique_stable().map(Column::from),
1413 Column::Scalar(s) => {
1414 _ = s.as_single_value_series().unique_stable()?;
1415 if s.is_empty() {
1416 return Ok(s.clone().into_column());
1417 }
1418
1419 Ok(s.resize(1).into_column())
1420 },
1421 }
1422 }
1423
1424 pub fn reshape_list(&self, dimensions: &[ReshapeDimension]) -> PolarsResult<Self> {
1425 self.as_materialized_series()
1427 .reshape_list(dimensions)
1428 .map(Self::from)
1429 }
1430
1431 #[cfg(feature = "dtype-array")]
1432 pub fn reshape_array(&self, dimensions: &[ReshapeDimension]) -> PolarsResult<Self> {
1433 self.as_materialized_series()
1435 .reshape_array(dimensions)
1436 .map(Self::from)
1437 }
1438
1439 pub fn sort(&self, sort_options: SortOptions) -> PolarsResult<Self> {
1440 self.as_materialized_series()
1442 .sort(sort_options)
1443 .map(Self::from)
1444 }
1445
1446 pub fn filter(&self, filter: &BooleanChunked) -> PolarsResult<Self> {
1447 match self {
1448 Column::Series(s) => s.filter(filter).map(Column::from),
1449 Column::Scalar(s) => {
1450 if s.is_empty() {
1451 return Ok(s.clone().into_column());
1452 }
1453
1454 if filter.len() == 1 {
1456 return match filter.get(0) {
1457 Some(true) => Ok(s.clone().into_column()),
1458 _ => Ok(s.resize(0).into_column()),
1459 };
1460 }
1461
1462 Ok(s.resize(filter.sum().unwrap() as usize).into_column())
1463 },
1464 }
1465 }
1466
1467 #[cfg(feature = "random")]
1468 pub fn shuffle(&self, seed: Option<u64>) -> Self {
1469 self.as_materialized_series().shuffle(seed).into()
1471 }
1472
1473 #[cfg(feature = "random")]
1474 pub fn sample_frac(
1475 &self,
1476 frac: f64,
1477 with_replacement: bool,
1478 shuffle: Option<bool>,
1479 seed: Option<u64>,
1480 ) -> PolarsResult<Self> {
1481 self.as_materialized_series()
1482 .sample_frac(frac, with_replacement, shuffle, seed)
1483 .map(Self::from)
1484 }
1485
1486 #[cfg(feature = "random")]
1487 pub fn sample_n(
1488 &self,
1489 n: usize,
1490 with_replacement: bool,
1491 shuffle: Option<bool>,
1492 seed: Option<u64>,
1493 ) -> PolarsResult<Self> {
1494 self.as_materialized_series()
1495 .sample_n(n, with_replacement, shuffle, seed)
1496 .map(Self::from)
1497 }
1498
1499 pub fn gather_every(&self, n: usize, offset: usize) -> PolarsResult<Column> {
1500 polars_ensure!(n > 0, InvalidOperation: "gather_every(n): n should be positive");
1501 if self.len().saturating_sub(offset) == 0 {
1502 return Ok(self.clear());
1503 }
1504
1505 match self {
1506 Column::Series(s) => Ok(s.gather_every(n, offset)?.into()),
1507 Column::Scalar(s) => {
1508 let total = s.len() - offset;
1509 Ok(s.resize(1 + (total - 1) / n).into())
1510 },
1511 }
1512 }
1513
1514 pub fn extend_constant(&self, value: AnyValue, n: usize) -> PolarsResult<Self> {
1515 if self.is_empty() {
1516 return Ok(Self::new_scalar(
1517 self.name().clone(),
1518 Scalar::new(self.dtype().clone(), value.into_static()),
1519 n,
1520 ));
1521 }
1522
1523 match self {
1524 Column::Series(s) => s.extend_constant(value, n).map(Column::from),
1525 Column::Scalar(s) => {
1526 if s.scalar().as_any_value() == value {
1527 Ok(s.resize(s.len() + n).into())
1528 } else {
1529 s.as_materialized_series()
1530 .extend_constant(value, n)
1531 .map(Column::from)
1532 }
1533 },
1534 }
1535 }
1536
1537 pub fn is_finite(&self) -> PolarsResult<BooleanChunked> {
1538 self.try_map_unary_elementwise_to_bool(|s| s.is_finite())
1539 }
1540 pub fn is_infinite(&self) -> PolarsResult<BooleanChunked> {
1541 self.try_map_unary_elementwise_to_bool(|s| s.is_infinite())
1542 }
1543 pub fn is_nan(&self) -> PolarsResult<BooleanChunked> {
1544 self.try_map_unary_elementwise_to_bool(|s| s.is_nan())
1545 }
1546 pub fn is_not_nan(&self) -> PolarsResult<BooleanChunked> {
1547 self.try_map_unary_elementwise_to_bool(|s| s.is_not_nan())
1548 }
1549
1550 pub fn wrapping_trunc_div_scalar<T>(&self, rhs: T) -> Self
1551 where
1552 T: Num + NumCast,
1553 {
1554 self.as_materialized_series()
1556 .wrapping_trunc_div_scalar(rhs)
1557 .into()
1558 }
1559
1560 pub fn product(&self) -> PolarsResult<Scalar> {
1561 self.as_materialized_series().product()
1563 }
1564
1565 #[inline]
1566 pub fn get(&self, index: usize) -> PolarsResult<AnyValue<'_>> {
1567 polars_ensure!(index < self.len(), oob = index, self.len());
1568
1569 Ok(unsafe { self.get_unchecked(index) })
1571 }
1572 #[inline(always)]
1576 pub unsafe fn get_unchecked(&self, index: usize) -> AnyValue<'_> {
1577 debug_assert!(index < self.len());
1578
1579 match self {
1580 Column::Series(s) => unsafe { s.get_unchecked(index) },
1581 Column::Scalar(s) => s.scalar().as_any_value(),
1582 }
1583 }
1584
1585 #[cfg(feature = "object")]
1586 pub fn get_object(
1587 &self,
1588 index: usize,
1589 ) -> Option<&dyn crate::chunked_array::object::PolarsObjectSafe> {
1590 self.as_materialized_series().get_object(index)
1591 }
1592
1593 pub fn bitand(&self, rhs: &Self) -> PolarsResult<Self> {
1594 self.try_apply_broadcasting_binary_elementwise(rhs, |l, r| l & r)
1595 }
1596 pub fn bitor(&self, rhs: &Self) -> PolarsResult<Self> {
1597 self.try_apply_broadcasting_binary_elementwise(rhs, |l, r| l | r)
1598 }
1599 pub fn bitxor(&self, rhs: &Self) -> PolarsResult<Self> {
1600 self.try_apply_broadcasting_binary_elementwise(rhs, |l, r| l ^ r)
1601 }
1602
1603 pub fn try_add_owned(self, other: Self) -> PolarsResult<Self> {
1604 match (self, other) {
1605 (Column::Series(lhs), Column::Series(rhs)) => {
1606 lhs.take().try_add_owned(rhs.take()).map(Column::from)
1607 },
1608 (lhs, rhs) => lhs + rhs,
1609 }
1610 }
1611 pub fn try_sub_owned(self, other: Self) -> PolarsResult<Self> {
1612 match (self, other) {
1613 (Column::Series(lhs), Column::Series(rhs)) => {
1614 lhs.take().try_sub_owned(rhs.take()).map(Column::from)
1615 },
1616 (lhs, rhs) => lhs - rhs,
1617 }
1618 }
1619 pub fn try_mul_owned(self, other: Self) -> PolarsResult<Self> {
1620 match (self, other) {
1621 (Column::Series(lhs), Column::Series(rhs)) => {
1622 lhs.take().try_mul_owned(rhs.take()).map(Column::from)
1623 },
1624 (lhs, rhs) => lhs * rhs,
1625 }
1626 }
1627
1628 pub(crate) fn str_value(&self, index: usize) -> PolarsResult<Cow<'_, str>> {
1629 Ok(self.get(index)?.str_value())
1630 }
1631
1632 pub fn min_reduce(&self) -> PolarsResult<Scalar> {
1633 match self {
1634 Column::Series(s) => s.min_reduce(),
1635 Column::Scalar(s) => {
1636 s.as_single_value_series().min_reduce()
1639 },
1640 }
1641 }
1642 pub fn max_reduce(&self) -> PolarsResult<Scalar> {
1643 match self {
1644 Column::Series(s) => s.max_reduce(),
1645 Column::Scalar(s) => {
1646 s.as_single_value_series().max_reduce()
1649 },
1650 }
1651 }
1652 pub fn median_reduce(&self) -> PolarsResult<Scalar> {
1653 match self {
1654 Column::Series(s) => s.median_reduce(),
1655 Column::Scalar(s) => {
1656 s.as_single_value_series().median_reduce()
1659 },
1660 }
1661 }
1662 pub fn mean_reduce(&self) -> PolarsResult<Scalar> {
1663 match self {
1664 Column::Series(s) => s.mean_reduce(),
1665 Column::Scalar(s) => {
1666 s.as_single_value_series().mean_reduce()
1669 },
1670 }
1671 }
1672 pub fn std_reduce(&self, ddof: u8) -> PolarsResult<Scalar> {
1673 match self {
1674 Column::Series(s) => s.std_reduce(ddof),
1675 Column::Scalar(s) => {
1676 let n = s.len().min(ddof as usize + 1);
1679 s.as_n_values_series(n).std_reduce(ddof)
1680 },
1681 }
1682 }
1683 pub fn var_reduce(&self, ddof: u8) -> PolarsResult<Scalar> {
1684 match self {
1685 Column::Series(s) => s.var_reduce(ddof),
1686 Column::Scalar(s) => {
1687 let n = s.len().min(ddof as usize + 1);
1690 s.as_n_values_series(n).var_reduce(ddof)
1691 },
1692 }
1693 }
1694 pub fn sum_reduce(&self) -> PolarsResult<Scalar> {
1695 self.as_materialized_series().sum_reduce()
1697 }
1698 pub fn and_reduce(&self) -> PolarsResult<Scalar> {
1699 match self {
1700 Column::Series(s) => s.and_reduce(),
1701 Column::Scalar(s) => {
1702 s.as_single_value_series().and_reduce()
1705 },
1706 }
1707 }
1708 pub fn or_reduce(&self) -> PolarsResult<Scalar> {
1709 match self {
1710 Column::Series(s) => s.or_reduce(),
1711 Column::Scalar(s) => {
1712 s.as_single_value_series().or_reduce()
1715 },
1716 }
1717 }
1718 pub fn xor_reduce(&self) -> PolarsResult<Scalar> {
1719 match self {
1720 Column::Series(s) => s.xor_reduce(),
1721 Column::Scalar(s) => {
1722 s.as_n_values_series(2 - s.len() % 2).xor_reduce()
1729 },
1730 }
1731 }
1732 pub fn n_unique(&self) -> PolarsResult<usize> {
1733 match self {
1734 Column::Series(s) => s.n_unique(),
1735 Column::Scalar(s) => s.as_single_value_series().n_unique(),
1736 }
1737 }
1738
1739 pub fn quantile_reduce(&self, quantile: f64, method: QuantileMethod) -> PolarsResult<Scalar> {
1740 self.as_materialized_series()
1741 .quantile_reduce(quantile, method)
1742 }
1743
1744 pub fn quantiles_reduce(
1745 &self,
1746 quantiles: &[f64],
1747 method: QuantileMethod,
1748 ) -> PolarsResult<Scalar> {
1749 self.as_materialized_series()
1750 .quantiles_reduce(quantiles, method)
1751 }
1752
1753 pub(crate) fn estimated_size(&self, expanded: bool) -> usize {
1754 match self {
1755 Column::Series(s) => s.estimated_size(),
1756 Column::Scalar(s) => s.estimated_size(expanded),
1757 }
1758 }
1759
1760 pub fn sort_with(&self, options: SortOptions) -> PolarsResult<Self> {
1761 match self {
1762 Column::Series(s) => s.sort_with(options).map(Self::from),
1763 Column::Scalar(s) => {
1764 _ = s.as_single_value_series().sort_with(options)?;
1766
1767 Ok(self.clone())
1768 },
1769 }
1770 }
1771
1772 pub fn map_unary_elementwise_to_bool(
1773 &self,
1774 f: impl Fn(&Series) -> BooleanChunked,
1775 ) -> BooleanChunked {
1776 self.try_map_unary_elementwise_to_bool(|s| Ok(f(s)))
1777 .unwrap()
1778 }
1779 pub fn try_map_unary_elementwise_to_bool(
1780 &self,
1781 f: impl Fn(&Series) -> PolarsResult<BooleanChunked>,
1782 ) -> PolarsResult<BooleanChunked> {
1783 match self {
1784 Column::Series(s) => f(s),
1785 Column::Scalar(s) => Ok(f(&s.as_single_value_series())?.new_from_index(0, s.len())),
1786 }
1787 }
1788
1789 pub fn apply_unary_elementwise(&self, f: impl Fn(&Series) -> Series) -> Column {
1790 self.try_apply_unary_elementwise(|s| Ok(f(s))).unwrap()
1791 }
1792 pub fn try_apply_unary_elementwise(
1793 &self,
1794 f: impl Fn(&Series) -> PolarsResult<Series>,
1795 ) -> PolarsResult<Column> {
1796 match self {
1797 Column::Series(s) => f(s).map(Column::from),
1798 Column::Scalar(s) => Ok(ScalarColumn::from_single_value_series(
1799 f(&s.as_single_value_series())?,
1800 s.len(),
1801 )
1802 .into()),
1803 }
1804 }
1805
1806 pub fn apply_broadcasting_binary_elementwise(
1807 &self,
1808 other: &Self,
1809 op: impl Fn(&Series, &Series) -> Series,
1810 ) -> PolarsResult<Column> {
1811 self.try_apply_broadcasting_binary_elementwise(other, |lhs, rhs| Ok(op(lhs, rhs)))
1812 }
1813 pub fn try_apply_broadcasting_binary_elementwise(
1814 &self,
1815 other: &Self,
1816 op: impl Fn(&Series, &Series) -> PolarsResult<Series>,
1817 ) -> PolarsResult<Column> {
1818 let length = broadcast_len([self, other])
1820 .context("cannot do a binary operation on columns of different lengths")?;
1821 match (self, other) {
1822 (Column::Series(lhs), Column::Series(rhs)) => op(lhs, rhs).map(Column::from),
1823 (Column::Series(lhs), Column::Scalar(rhs)) => {
1824 op(lhs, &rhs.as_single_value_series()).map(Column::from)
1825 },
1826 (Column::Scalar(lhs), Column::Series(rhs)) => {
1827 op(&lhs.as_single_value_series(), rhs).map(Column::from)
1828 },
1829 (Column::Scalar(lhs), Column::Scalar(rhs)) => {
1830 let lhs = lhs.as_single_value_series();
1831 let rhs = rhs.as_single_value_series();
1832
1833 Ok(ScalarColumn::from_single_value_series(op(&lhs, &rhs)?, length).into_column())
1834 },
1835 }
1836 }
1837
1838 pub fn apply_binary_elementwise(
1839 &self,
1840 other: &Self,
1841 f: impl Fn(&Series, &Series) -> Series,
1842 f_lb: impl Fn(&Scalar, &Series) -> Series,
1843 f_rb: impl Fn(&Series, &Scalar) -> Series,
1844 ) -> Column {
1845 self.try_apply_binary_elementwise(
1846 other,
1847 |lhs, rhs| Ok(f(lhs, rhs)),
1848 |lhs, rhs| Ok(f_lb(lhs, rhs)),
1849 |lhs, rhs| Ok(f_rb(lhs, rhs)),
1850 )
1851 .unwrap()
1852 }
1853 pub fn try_apply_binary_elementwise(
1854 &self,
1855 other: &Self,
1856 f: impl Fn(&Series, &Series) -> PolarsResult<Series>,
1857 f_lb: impl Fn(&Scalar, &Series) -> PolarsResult<Series>,
1858 f_rb: impl Fn(&Series, &Scalar) -> PolarsResult<Series>,
1859 ) -> PolarsResult<Column> {
1860 debug_assert_eq!(self.len(), other.len());
1861
1862 match (self, other) {
1863 (Column::Series(lhs), Column::Series(rhs)) => f(lhs, rhs).map(Column::from),
1864 (Column::Series(lhs), Column::Scalar(rhs)) => f_rb(lhs, rhs.scalar()).map(Column::from),
1865 (Column::Scalar(lhs), Column::Series(rhs)) => f_lb(lhs.scalar(), rhs).map(Column::from),
1866 (Column::Scalar(lhs), Column::Scalar(rhs)) => {
1867 let lhs = lhs.as_single_value_series();
1868 let rhs = rhs.as_single_value_series();
1869
1870 Ok(
1871 ScalarColumn::from_single_value_series(f(&lhs, &rhs)?, self.len())
1872 .into_column(),
1873 )
1874 },
1875 }
1876 }
1877
1878 #[cfg(feature = "approx_unique")]
1879 pub fn approx_n_unique(&self) -> PolarsResult<IdxSize> {
1880 match self {
1881 Column::Series(s) => s.approx_n_unique(),
1882 Column::Scalar(s) => {
1883 s.as_single_value_series().approx_n_unique()?;
1885 Ok(1)
1886 },
1887 }
1888 }
1889
1890 pub fn n_chunks(&self) -> usize {
1891 match self {
1892 Column::Series(s) => s.n_chunks(),
1893 Column::Scalar(s) => s.lazy_as_materialized_series().map_or(1, |x| x.n_chunks()),
1895 }
1896 }
1897
1898 #[expect(clippy::wrong_self_convention)]
1899 pub(crate) fn into_total_ord_inner<'a>(&'a self) -> Box<dyn TotalOrdInner + 'a> {
1900 self.as_materialized_series().into_total_ord_inner()
1902 }
1903
1904 pub fn rechunk_to_arrow(self, compat_level: CompatLevel) -> Box<dyn Array> {
1905 let mut series = self.take_materialized_series();
1907 if series.n_chunks() > 1 {
1908 series = series.rechunk();
1909 }
1910 series.to_arrow(0, compat_level)
1911 }
1912
1913 pub fn trim_lists_to_normalized_offsets(&self) -> Option<Column> {
1914 self.as_materialized_series()
1915 .trim_lists_to_normalized_offsets()
1916 .map(Column::from)
1917 }
1918
1919 pub fn propagate_nulls(&self) -> Option<Column> {
1920 self.as_materialized_series()
1921 .propagate_nulls()
1922 .map(Column::from)
1923 }
1924
1925 pub fn deposit(&self, validity: &Bitmap) -> Column {
1926 self.as_materialized_series()
1927 .deposit(validity)
1928 .into_column()
1929 }
1930
1931 pub fn rechunk_validity(&self) -> Option<Bitmap> {
1932 self.as_materialized_series().rechunk_validity()
1934 }
1935
1936 pub fn unique_id(&self) -> PolarsResult<(IdxSize, Vec<IdxSize>)> {
1937 self.as_materialized_series().unique_id()
1938 }
1939}
1940
1941impl Default for Column {
1942 fn default() -> Self {
1943 Self::new_scalar(
1944 PlSmallStr::EMPTY,
1945 Scalar::new(DataType::Int64, AnyValue::Null),
1946 0,
1947 )
1948 }
1949}
1950
1951impl PartialEq for Column {
1952 fn eq(&self, other: &Self) -> bool {
1953 self.as_materialized_series()
1955 .eq(other.as_materialized_series())
1956 }
1957}
1958
1959impl From<Series> for Column {
1960 #[inline]
1961 fn from(series: Series) -> Self {
1962 if series.len() == 1 {
1965 return Self::Scalar(ScalarColumn::unit_scalar_from_series(series));
1966 }
1967
1968 Self::Series(SeriesColumn::new(series))
1969 }
1970}
1971
1972impl<T: IntoSeries> IntoColumn for T {
1973 #[inline]
1974 fn into_column(self) -> Column {
1975 self.into_series().into()
1976 }
1977}
1978
1979impl IntoColumn for Column {
1980 #[inline(always)]
1981 fn into_column(self) -> Column {
1982 self
1983 }
1984}
1985
1986impl BroadcastLength for Column {
1987 fn _broadcast_len(&self) -> usize {
1988 self.len()
1989 }
1990
1991 fn _column_name(&self) -> Option<&str> {
1992 Some(self.name())
1993 }
1994}
1995
1996#[derive(Clone)]
2001#[cfg_attr(feature = "serde", derive(serde::Serialize))]
2002#[cfg_attr(feature = "serde", serde(into = "Series"))]
2003struct _SerdeSeries(Series);
2004
2005impl From<Column> for _SerdeSeries {
2006 #[inline]
2007 fn from(value: Column) -> Self {
2008 Self(value.take_materialized_series())
2009 }
2010}
2011
2012impl From<_SerdeSeries> for Series {
2013 #[inline]
2014 fn from(value: _SerdeSeries) -> Self {
2015 value.0
2016 }
2017}