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