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 #[cfg(feature = "dtype-u128")]
339 pub fn try_u128(&self) -> Option<&UInt128Chunked> {
340 self.as_materialized_series().try_u128()
341 }
342 pub fn try_f32(&self) -> Option<&Float32Chunked> {
343 self.as_materialized_series().try_f32()
344 }
345 pub fn try_f64(&self) -> Option<&Float64Chunked> {
346 self.as_materialized_series().try_f64()
347 }
348 pub fn try_str(&self) -> Option<&StringChunked> {
349 self.as_materialized_series().try_str()
350 }
351 pub fn try_list(&self) -> Option<&ListChunked> {
352 self.as_materialized_series().try_list()
353 }
354 pub fn try_binary(&self) -> Option<&BinaryChunked> {
355 self.as_materialized_series().try_binary()
356 }
357 pub fn try_idx(&self) -> Option<&IdxCa> {
358 self.as_materialized_series().try_idx()
359 }
360 pub fn try_binary_offset(&self) -> Option<&BinaryOffsetChunked> {
361 self.as_materialized_series().try_binary_offset()
362 }
363 #[cfg(feature = "dtype-datetime")]
364 pub fn try_datetime(&self) -> Option<&DatetimeChunked> {
365 self.as_materialized_series().try_datetime()
366 }
367 #[cfg(feature = "dtype-struct")]
368 pub fn try_struct(&self) -> Option<&StructChunked> {
369 self.as_materialized_series().try_struct()
370 }
371 #[cfg(feature = "dtype-decimal")]
372 pub fn try_decimal(&self) -> Option<&DecimalChunked> {
373 self.as_materialized_series().try_decimal()
374 }
375 #[cfg(feature = "dtype-array")]
376 pub fn try_array(&self) -> Option<&ArrayChunked> {
377 self.as_materialized_series().try_array()
378 }
379 #[cfg(feature = "dtype-categorical")]
380 pub fn try_cat<T: PolarsCategoricalType>(&self) -> Option<&CategoricalChunked<T>> {
381 self.as_materialized_series().try_cat::<T>()
382 }
383 #[cfg(feature = "dtype-categorical")]
384 pub fn try_cat8(&self) -> Option<&Categorical8Chunked> {
385 self.as_materialized_series().try_cat8()
386 }
387 #[cfg(feature = "dtype-categorical")]
388 pub fn try_cat16(&self) -> Option<&Categorical16Chunked> {
389 self.as_materialized_series().try_cat16()
390 }
391 #[cfg(feature = "dtype-categorical")]
392 pub fn try_cat32(&self) -> Option<&Categorical32Chunked> {
393 self.as_materialized_series().try_cat32()
394 }
395 #[cfg(feature = "dtype-date")]
396 pub fn try_date(&self) -> Option<&DateChunked> {
397 self.as_materialized_series().try_date()
398 }
399 #[cfg(feature = "dtype-duration")]
400 pub fn try_duration(&self) -> Option<&DurationChunked> {
401 self.as_materialized_series().try_duration()
402 }
403
404 pub fn bool(&self) -> PolarsResult<&BooleanChunked> {
406 self.as_materialized_series().bool()
407 }
408 pub fn i8(&self) -> PolarsResult<&Int8Chunked> {
409 self.as_materialized_series().i8()
410 }
411 pub fn i16(&self) -> PolarsResult<&Int16Chunked> {
412 self.as_materialized_series().i16()
413 }
414 pub fn i32(&self) -> PolarsResult<&Int32Chunked> {
415 self.as_materialized_series().i32()
416 }
417 pub fn i64(&self) -> PolarsResult<&Int64Chunked> {
418 self.as_materialized_series().i64()
419 }
420 #[cfg(feature = "dtype-i128")]
421 pub fn i128(&self) -> PolarsResult<&Int128Chunked> {
422 self.as_materialized_series().i128()
423 }
424 pub fn u8(&self) -> PolarsResult<&UInt8Chunked> {
425 self.as_materialized_series().u8()
426 }
427 pub fn u16(&self) -> PolarsResult<&UInt16Chunked> {
428 self.as_materialized_series().u16()
429 }
430 pub fn u32(&self) -> PolarsResult<&UInt32Chunked> {
431 self.as_materialized_series().u32()
432 }
433 pub fn u64(&self) -> PolarsResult<&UInt64Chunked> {
434 self.as_materialized_series().u64()
435 }
436 #[cfg(feature = "dtype-u128")]
437 pub fn u128(&self) -> PolarsResult<&UInt128Chunked> {
438 self.as_materialized_series().u128()
439 }
440 pub fn f32(&self) -> PolarsResult<&Float32Chunked> {
441 self.as_materialized_series().f32()
442 }
443 pub fn f64(&self) -> PolarsResult<&Float64Chunked> {
444 self.as_materialized_series().f64()
445 }
446 pub fn str(&self) -> PolarsResult<&StringChunked> {
447 self.as_materialized_series().str()
448 }
449 pub fn list(&self) -> PolarsResult<&ListChunked> {
450 self.as_materialized_series().list()
451 }
452 pub fn binary(&self) -> PolarsResult<&BinaryChunked> {
453 self.as_materialized_series().binary()
454 }
455 pub fn idx(&self) -> PolarsResult<&IdxCa> {
456 self.as_materialized_series().idx()
457 }
458 pub fn binary_offset(&self) -> PolarsResult<&BinaryOffsetChunked> {
459 self.as_materialized_series().binary_offset()
460 }
461 #[cfg(feature = "dtype-datetime")]
462 pub fn datetime(&self) -> PolarsResult<&DatetimeChunked> {
463 self.as_materialized_series().datetime()
464 }
465 #[cfg(feature = "dtype-struct")]
466 pub fn struct_(&self) -> PolarsResult<&StructChunked> {
467 self.as_materialized_series().struct_()
468 }
469 #[cfg(feature = "dtype-decimal")]
470 pub fn decimal(&self) -> PolarsResult<&DecimalChunked> {
471 self.as_materialized_series().decimal()
472 }
473 #[cfg(feature = "dtype-array")]
474 pub fn array(&self) -> PolarsResult<&ArrayChunked> {
475 self.as_materialized_series().array()
476 }
477 #[cfg(feature = "dtype-categorical")]
478 pub fn cat<T: PolarsCategoricalType>(&self) -> PolarsResult<&CategoricalChunked<T>> {
479 self.as_materialized_series().cat::<T>()
480 }
481 #[cfg(feature = "dtype-categorical")]
482 pub fn cat8(&self) -> PolarsResult<&Categorical8Chunked> {
483 self.as_materialized_series().cat8()
484 }
485 #[cfg(feature = "dtype-categorical")]
486 pub fn cat16(&self) -> PolarsResult<&Categorical16Chunked> {
487 self.as_materialized_series().cat16()
488 }
489 #[cfg(feature = "dtype-categorical")]
490 pub fn cat32(&self) -> PolarsResult<&Categorical32Chunked> {
491 self.as_materialized_series().cat32()
492 }
493 #[cfg(feature = "dtype-date")]
494 pub fn date(&self) -> PolarsResult<&DateChunked> {
495 self.as_materialized_series().date()
496 }
497 #[cfg(feature = "dtype-duration")]
498 pub fn duration(&self) -> PolarsResult<&DurationChunked> {
499 self.as_materialized_series().duration()
500 }
501
502 pub fn cast_with_options(&self, dtype: &DataType, options: CastOptions) -> PolarsResult<Self> {
504 match self {
505 Column::Series(s) => s.cast_with_options(dtype, options).map(Column::from),
506 Column::Partitioned(s) => s.cast_with_options(dtype, options).map(Column::from),
507 Column::Scalar(s) => s.cast_with_options(dtype, options).map(Column::from),
508 }
509 }
510 pub fn strict_cast(&self, dtype: &DataType) -> PolarsResult<Self> {
511 match self {
512 Column::Series(s) => s.strict_cast(dtype).map(Column::from),
513 Column::Partitioned(s) => s.strict_cast(dtype).map(Column::from),
514 Column::Scalar(s) => s.strict_cast(dtype).map(Column::from),
515 }
516 }
517 pub fn cast(&self, dtype: &DataType) -> PolarsResult<Column> {
518 match self {
519 Column::Series(s) => s.cast(dtype).map(Column::from),
520 Column::Partitioned(s) => s.cast(dtype).map(Column::from),
521 Column::Scalar(s) => s.cast(dtype).map(Column::from),
522 }
523 }
524 pub unsafe fn cast_unchecked(&self, dtype: &DataType) -> PolarsResult<Column> {
528 match self {
529 Column::Series(s) => unsafe { s.cast_unchecked(dtype) }.map(Column::from),
530 Column::Partitioned(s) => unsafe { s.cast_unchecked(dtype) }.map(Column::from),
531 Column::Scalar(s) => unsafe { s.cast_unchecked(dtype) }.map(Column::from),
532 }
533 }
534
535 pub fn clear(&self) -> Self {
536 match self {
537 Column::Series(s) => s.clear().into(),
538 Column::Partitioned(s) => s.clear().into(),
539 Column::Scalar(s) => s.resize(0).into(),
540 }
541 }
542
543 #[inline]
544 pub fn shrink_to_fit(&mut self) {
545 match self {
546 Column::Series(s) => s.shrink_to_fit(),
547 Column::Partitioned(_) => {},
549 Column::Scalar(_) => {},
550 }
551 }
552
553 #[inline]
554 pub fn new_from_index(&self, index: usize, length: usize) -> Self {
555 if index >= self.len() {
556 return Self::full_null(self.name().clone(), length, self.dtype());
557 }
558
559 match self {
560 Column::Series(s) => {
561 let av = unsafe { s.get_unchecked(index) };
563 let scalar = Scalar::new(self.dtype().clone(), av.into_static());
564 Self::new_scalar(self.name().clone(), scalar, length)
565 },
566 Column::Partitioned(s) => {
567 let av = unsafe { s.get_unchecked(index) };
569 let scalar = Scalar::new(self.dtype().clone(), av.into_static());
570 Self::new_scalar(self.name().clone(), scalar, length)
571 },
572 Column::Scalar(s) => s.resize(length).into(),
573 }
574 }
575
576 #[inline]
577 pub fn has_nulls(&self) -> bool {
578 match self {
579 Self::Series(s) => s.has_nulls(),
580 Self::Partitioned(s) => s.as_materialized_series().has_nulls(),
582 Self::Scalar(s) => s.has_nulls(),
583 }
584 }
585
586 #[inline]
587 pub fn is_null(&self) -> BooleanChunked {
588 match self {
589 Self::Series(s) => s.is_null(),
590 Self::Partitioned(s) => s.as_materialized_series().is_null(),
592 Self::Scalar(s) => {
593 BooleanChunked::full(s.name().clone(), s.scalar().is_null(), s.len())
594 },
595 }
596 }
597 #[inline]
598 pub fn is_not_null(&self) -> BooleanChunked {
599 match self {
600 Self::Series(s) => s.is_not_null(),
601 Self::Partitioned(s) => s.as_materialized_series().is_not_null(),
603 Self::Scalar(s) => {
604 BooleanChunked::full(s.name().clone(), !s.scalar().is_null(), s.len())
605 },
606 }
607 }
608
609 pub fn to_physical_repr(&self) -> Column {
610 self.as_materialized_series()
612 .to_physical_repr()
613 .into_owned()
614 .into()
615 }
616 pub unsafe fn from_physical_unchecked(&self, dtype: &DataType) -> PolarsResult<Column> {
620 self.as_materialized_series()
622 .from_physical_unchecked(dtype)
623 .map(Column::from)
624 }
625
626 pub fn head(&self, length: Option<usize>) -> Column {
627 let len = length.unwrap_or(HEAD_DEFAULT_LENGTH);
628 let len = usize::min(len, self.len());
629 self.slice(0, len)
630 }
631 pub fn tail(&self, length: Option<usize>) -> Column {
632 let len = length.unwrap_or(TAIL_DEFAULT_LENGTH);
633 let len = usize::min(len, self.len());
634 debug_assert!(len <= i64::MAX as usize);
635 self.slice(-(len as i64), len)
636 }
637 pub fn slice(&self, offset: i64, length: usize) -> Column {
638 match self {
639 Column::Series(s) => s.slice(offset, length).into(),
640 Column::Partitioned(s) => s.as_materialized_series().slice(offset, length).into(),
642 Column::Scalar(s) => {
643 let (_, length) = slice_offsets(offset, length, s.len());
644 s.resize(length).into()
645 },
646 }
647 }
648
649 pub fn split_at(&self, offset: i64) -> (Column, Column) {
650 let (l, r) = self.as_materialized_series().split_at(offset);
652 (l.into(), r.into())
653 }
654
655 #[inline]
656 pub fn null_count(&self) -> usize {
657 match self {
658 Self::Series(s) => s.null_count(),
659 Self::Partitioned(s) => s.null_count(),
660 Self::Scalar(s) if s.scalar().is_null() => s.len(),
661 Self::Scalar(_) => 0,
662 }
663 }
664
665 pub fn take(&self, indices: &IdxCa) -> PolarsResult<Column> {
666 check_bounds_ca(indices, self.len() as IdxSize)?;
667 Ok(unsafe { self.take_unchecked(indices) })
668 }
669 pub fn take_slice(&self, indices: &[IdxSize]) -> PolarsResult<Column> {
670 check_bounds(indices, self.len() as IdxSize)?;
671 Ok(unsafe { self.take_slice_unchecked(indices) })
672 }
673 pub unsafe fn take_unchecked(&self, indices: &IdxCa) -> Column {
677 debug_assert!(check_bounds_ca(indices, self.len() as IdxSize).is_ok());
678
679 match self {
680 Self::Series(s) => unsafe { s.take_unchecked(indices) }.into(),
681 Self::Partitioned(s) => {
682 let s = s.as_materialized_series();
683 unsafe { s.take_unchecked(indices) }.into()
684 },
685 Self::Scalar(s) => {
686 let idxs_length = indices.len();
687 let idxs_null_count = indices.null_count();
688
689 let scalar = ScalarColumn::from_single_value_series(
690 s.as_single_value_series().take_unchecked(&IdxCa::new(
691 indices.name().clone(),
692 &[0][..s.len().min(1)],
693 )),
694 idxs_length,
695 );
696
697 if idxs_null_count == 0 || scalar.has_nulls() {
699 scalar.into_column()
700 } else if idxs_null_count == idxs_length {
701 scalar.into_nulls().into_column()
702 } else {
703 let validity = indices.rechunk_validity();
704 let series = scalar.take_materialized_series();
705 let name = series.name().clone();
706 let dtype = series.dtype().clone();
707 let mut chunks = series.into_chunks();
708 assert_eq!(chunks.len(), 1);
709 chunks[0] = chunks[0].with_validity(validity);
710 unsafe { Series::from_chunks_and_dtype_unchecked(name, chunks, &dtype) }
711 .into_column()
712 }
713 },
714 }
715 }
716 pub unsafe fn take_slice_unchecked(&self, indices: &[IdxSize]) -> Column {
720 debug_assert!(check_bounds(indices, self.len() as IdxSize).is_ok());
721
722 match self {
723 Self::Series(s) => unsafe { s.take_slice_unchecked(indices) }.into(),
724 Self::Partitioned(s) => {
725 let s = s.as_materialized_series();
726 unsafe { s.take_slice_unchecked(indices) }.into()
727 },
728 Self::Scalar(s) => ScalarColumn::from_single_value_series(
729 s.as_single_value_series()
730 .take_slice_unchecked(&[0][..s.len().min(1)]),
731 indices.len(),
732 )
733 .into(),
734 }
735 }
736
737 #[inline(always)]
739 #[cfg(any(feature = "algorithm_group_by", feature = "bitwise"))]
740 fn agg_with_unit_scalar(
741 &self,
742 groups: &GroupsType,
743 series_agg: impl Fn(&Series, &GroupsType) -> Series,
744 ) -> Column {
745 match self {
746 Column::Series(s) => series_agg(s, groups).into_column(),
747 Column::Partitioned(s) => series_agg(s.as_materialized_series(), groups).into_column(),
749 Column::Scalar(s) => {
750 if s.is_empty() {
751 return series_agg(s.as_materialized_series(), groups).into_column();
752 }
753
754 let series_aggregation = series_agg(
758 &s.as_single_value_series(),
759 &GroupsType::Slice {
760 groups: vec![[0, 1]],
762 rolling: false,
763 },
764 );
765
766 if series_aggregation.has_nulls() {
768 return Self::new_scalar(
769 series_aggregation.name().clone(),
770 Scalar::new(series_aggregation.dtype().clone(), AnyValue::Null),
771 groups.len(),
772 );
773 }
774
775 let mut scalar_col = s.resize(groups.len());
776 if series_aggregation.dtype() != s.dtype() {
779 scalar_col = scalar_col.cast(series_aggregation.dtype()).unwrap();
780 }
781
782 let Some(first_empty_idx) = groups.iter().position(|g| g.is_empty()) else {
783 return scalar_col.into_column();
785 };
786
787 let mut validity = BitmapBuilder::with_capacity(groups.len());
789 validity.extend_constant(first_empty_idx, true);
790 let iter = unsafe {
792 TrustMyLength::new(
793 groups.iter().skip(first_empty_idx).map(|g| !g.is_empty()),
794 groups.len() - first_empty_idx,
795 )
796 };
797 validity.extend_trusted_len_iter(iter);
798
799 let mut s = scalar_col.take_materialized_series().rechunk();
800 let chunks = unsafe { s.chunks_mut() };
802 let arr = &mut chunks[0];
803 *arr = arr.with_validity(validity.into_opt_validity());
804 s.compute_len();
805
806 s.into_column()
807 },
808 }
809 }
810
811 #[cfg(feature = "algorithm_group_by")]
815 pub unsafe fn agg_min(&self, groups: &GroupsType) -> Self {
816 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_min(g) })
817 }
818
819 #[cfg(feature = "algorithm_group_by")]
823 pub unsafe fn agg_max(&self, groups: &GroupsType) -> Self {
824 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_max(g) })
825 }
826
827 #[cfg(feature = "algorithm_group_by")]
831 pub unsafe fn agg_mean(&self, groups: &GroupsType) -> Self {
832 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_mean(g) })
833 }
834
835 #[cfg(feature = "algorithm_group_by")]
839 pub unsafe fn agg_sum(&self, groups: &GroupsType) -> Self {
840 unsafe { self.as_materialized_series().agg_sum(groups) }.into()
842 }
843
844 #[cfg(feature = "algorithm_group_by")]
848 pub unsafe fn agg_first(&self, groups: &GroupsType) -> Self {
849 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_first(g) })
850 }
851
852 #[cfg(feature = "algorithm_group_by")]
856 pub unsafe fn agg_last(&self, groups: &GroupsType) -> Self {
857 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_last(g) })
858 }
859
860 #[cfg(feature = "algorithm_group_by")]
864 pub unsafe fn agg_n_unique(&self, groups: &GroupsType) -> Self {
865 unsafe { self.as_materialized_series().agg_n_unique(groups) }.into()
867 }
868
869 #[cfg(feature = "algorithm_group_by")]
873 pub unsafe fn agg_quantile(
874 &self,
875 groups: &GroupsType,
876 quantile: f64,
877 method: QuantileMethod,
878 ) -> Self {
879 unsafe {
882 self.as_materialized_series()
883 .agg_quantile(groups, quantile, method)
884 }
885 .into()
886 }
887
888 #[cfg(feature = "algorithm_group_by")]
892 pub unsafe fn agg_median(&self, groups: &GroupsType) -> Self {
893 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_median(g) })
894 }
895
896 #[cfg(feature = "algorithm_group_by")]
900 pub unsafe fn agg_var(&self, groups: &GroupsType, ddof: u8) -> Self {
901 unsafe { self.as_materialized_series().agg_var(groups, ddof) }.into()
903 }
904
905 #[cfg(feature = "algorithm_group_by")]
909 pub unsafe fn agg_std(&self, groups: &GroupsType, ddof: u8) -> Self {
910 unsafe { self.as_materialized_series().agg_std(groups, ddof) }.into()
912 }
913
914 #[cfg(feature = "algorithm_group_by")]
918 pub unsafe fn agg_list(&self, groups: &GroupsType) -> Self {
919 unsafe { self.as_materialized_series().agg_list(groups) }.into()
921 }
922
923 #[cfg(feature = "algorithm_group_by")]
927 pub fn agg_valid_count(&self, groups: &GroupsType) -> Self {
928 unsafe { self.as_materialized_series().agg_valid_count(groups) }.into()
931 }
932
933 #[cfg(feature = "bitwise")]
937 pub fn agg_and(&self, groups: &GroupsType) -> Self {
938 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_and(g) })
939 }
940 #[cfg(feature = "bitwise")]
944 pub fn agg_or(&self, groups: &GroupsType) -> Self {
945 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_or(g) })
946 }
947 #[cfg(feature = "bitwise")]
951 pub fn agg_xor(&self, groups: &GroupsType) -> Self {
952 unsafe { self.as_materialized_series().agg_xor(groups) }.into()
955 }
956
957 pub fn full_null(name: PlSmallStr, size: usize, dtype: &DataType) -> Self {
958 Self::new_scalar(name, Scalar::new(dtype.clone(), AnyValue::Null), size)
959 }
960
961 pub fn is_empty(&self) -> bool {
962 self.len() == 0
963 }
964
965 pub fn reverse(&self) -> Column {
966 match self {
967 Column::Series(s) => s.reverse().into(),
968 Column::Partitioned(s) => s.reverse().into(),
969 Column::Scalar(_) => self.clone(),
970 }
971 }
972
973 pub fn equals(&self, other: &Column) -> bool {
974 self.as_materialized_series()
976 .equals(other.as_materialized_series())
977 }
978
979 pub fn equals_missing(&self, other: &Column) -> bool {
980 self.as_materialized_series()
982 .equals_missing(other.as_materialized_series())
983 }
984
985 pub fn set_sorted_flag(&mut self, sorted: IsSorted) {
986 match self {
988 Column::Series(s) => s.set_sorted_flag(sorted),
989 Column::Partitioned(s) => s.set_sorted_flag(sorted),
990 Column::Scalar(_) => {},
991 }
992 }
993
994 pub fn get_flags(&self) -> StatisticsFlags {
995 match self {
996 Column::Series(s) => s.get_flags(),
997 Column::Partitioned(_) => StatisticsFlags::empty(),
999 Column::Scalar(_) => {
1000 StatisticsFlags::IS_SORTED_ASC | StatisticsFlags::CAN_FAST_EXPLODE_LIST
1001 },
1002 }
1003 }
1004
1005 pub fn set_flags(&mut self, flags: StatisticsFlags) -> bool {
1007 match self {
1008 Column::Series(s) => {
1009 s.set_flags(flags);
1010 true
1011 },
1012 Column::Partitioned(_) => false,
1014 Column::Scalar(_) => false,
1015 }
1016 }
1017
1018 pub fn vec_hash(
1019 &self,
1020 build_hasher: PlSeedableRandomStateQuality,
1021 buf: &mut Vec<u64>,
1022 ) -> PolarsResult<()> {
1023 self.as_materialized_series().vec_hash(build_hasher, buf)
1025 }
1026
1027 pub fn vec_hash_combine(
1028 &self,
1029 build_hasher: PlSeedableRandomStateQuality,
1030 hashes: &mut [u64],
1031 ) -> PolarsResult<()> {
1032 self.as_materialized_series()
1034 .vec_hash_combine(build_hasher, hashes)
1035 }
1036
1037 pub fn append(&mut self, other: &Column) -> PolarsResult<&mut Self> {
1038 self.into_materialized_series()
1040 .append(other.as_materialized_series())?;
1041 Ok(self)
1042 }
1043 pub fn append_owned(&mut self, other: Column) -> PolarsResult<&mut Self> {
1044 self.into_materialized_series()
1045 .append_owned(other.take_materialized_series())?;
1046 Ok(self)
1047 }
1048
1049 pub fn arg_sort(&self, options: SortOptions) -> IdxCa {
1050 if self.is_empty() {
1051 return IdxCa::from_vec(self.name().clone(), Vec::new());
1052 }
1053
1054 if self.null_count() == self.len() {
1055 let values = if options.descending {
1057 (0..self.len() as IdxSize).rev().collect()
1058 } else {
1059 (0..self.len() as IdxSize).collect()
1060 };
1061
1062 return IdxCa::from_vec(self.name().clone(), values);
1063 }
1064
1065 let is_sorted = Some(self.is_sorted_flag());
1066 let Some(is_sorted) = is_sorted.filter(|v| !matches!(v, IsSorted::Not)) else {
1067 return self.as_materialized_series().arg_sort(options);
1068 };
1069
1070 let is_sorted_dsc = matches!(is_sorted, IsSorted::Descending);
1072 let invert = options.descending != is_sorted_dsc;
1073
1074 let mut values = Vec::with_capacity(self.len());
1075
1076 #[inline(never)]
1077 fn extend(
1078 start: IdxSize,
1079 end: IdxSize,
1080 slf: &Column,
1081 values: &mut Vec<IdxSize>,
1082 is_only_nulls: bool,
1083 invert: bool,
1084 maintain_order: bool,
1085 ) {
1086 debug_assert!(start <= end);
1087 debug_assert!(start as usize <= slf.len());
1088 debug_assert!(end as usize <= slf.len());
1089
1090 if !invert || is_only_nulls {
1091 values.extend(start..end);
1092 return;
1093 }
1094
1095 if !maintain_order {
1097 values.extend((start..end).rev());
1098 return;
1099 }
1100
1101 let arg_unique = slf
1107 .slice(start as i64, (end - start) as usize)
1108 .arg_unique()
1109 .unwrap();
1110
1111 assert!(!arg_unique.has_nulls());
1112
1113 let num_unique = arg_unique.len();
1114
1115 if num_unique == (end - start) as usize {
1117 values.extend((start..end).rev());
1118 return;
1119 }
1120
1121 if num_unique == 1 {
1122 values.extend(start..end);
1123 return;
1124 }
1125
1126 let mut prev_idx = end - start;
1127 for chunk in arg_unique.downcast_iter() {
1128 for &idx in chunk.values().as_slice().iter().rev() {
1129 values.extend(start + idx..start + prev_idx);
1130 prev_idx = idx;
1131 }
1132 }
1133 }
1134 macro_rules! extend {
1135 ($start:expr, $end:expr) => {
1136 extend!($start, $end, is_only_nulls = false);
1137 };
1138 ($start:expr, $end:expr, is_only_nulls = $is_only_nulls:expr) => {
1139 extend(
1140 $start,
1141 $end,
1142 self,
1143 &mut values,
1144 $is_only_nulls,
1145 invert,
1146 options.maintain_order,
1147 );
1148 };
1149 }
1150
1151 let length = self.len() as IdxSize;
1152 let null_count = self.null_count() as IdxSize;
1153
1154 if null_count == 0 {
1155 extend!(0, length);
1156 } else {
1157 let has_nulls_last = self.get(self.len() - 1).unwrap().is_null();
1158 match (options.nulls_last, has_nulls_last) {
1159 (true, true) => {
1160 extend!(0, length - null_count);
1162 extend!(length - null_count, length, is_only_nulls = true);
1163 },
1164 (true, false) => {
1165 extend!(null_count, length);
1167 extend!(0, null_count, is_only_nulls = true);
1168 },
1169 (false, true) => {
1170 extend!(length - null_count, length, is_only_nulls = true);
1172 extend!(0, length - null_count);
1173 },
1174 (false, false) => {
1175 extend!(0, null_count, is_only_nulls = true);
1177 extend!(null_count, length);
1178 },
1179 }
1180 }
1181
1182 if let Some(limit) = options.limit {
1185 let limit = limit.min(length);
1186 values.truncate(limit as usize);
1187 }
1188
1189 IdxCa::from_vec(self.name().clone(), values)
1190 }
1191
1192 pub fn arg_sort_multiple(
1193 &self,
1194 by: &[Column],
1195 options: &SortMultipleOptions,
1196 ) -> PolarsResult<IdxCa> {
1197 self.as_materialized_series().arg_sort_multiple(by, options)
1199 }
1200
1201 pub fn arg_unique(&self) -> PolarsResult<IdxCa> {
1202 match self {
1203 Column::Scalar(s) => Ok(IdxCa::new_vec(s.name().clone(), vec![0])),
1204 _ => self.as_materialized_series().arg_unique(),
1205 }
1206 }
1207
1208 pub fn bit_repr(&self) -> Option<BitRepr> {
1209 self.as_materialized_series().bit_repr()
1211 }
1212
1213 pub fn into_frame(self) -> DataFrame {
1214 unsafe { DataFrame::new_no_checks(self.len(), vec![self]) }
1216 }
1217
1218 pub fn extend(&mut self, other: &Column) -> PolarsResult<&mut Self> {
1219 self.into_materialized_series()
1221 .extend(other.as_materialized_series())?;
1222 Ok(self)
1223 }
1224
1225 pub fn rechunk(&self) -> Column {
1226 match self {
1227 Column::Series(s) => s.rechunk().into(),
1228 Column::Partitioned(s) => {
1229 if let Some(s) = s.lazy_as_materialized_series() {
1230 debug_assert_eq!(s.n_chunks(), 1)
1232 }
1233 self.clone()
1234 },
1235 Column::Scalar(s) => {
1236 if s.lazy_as_materialized_series()
1237 .filter(|x| x.n_chunks() > 1)
1238 .is_some()
1239 {
1240 Column::Scalar(ScalarColumn::new(
1241 s.name().clone(),
1242 s.scalar().clone(),
1243 s.len(),
1244 ))
1245 } else {
1246 self.clone()
1247 }
1248 },
1249 }
1250 }
1251
1252 pub fn explode(&self, skip_empty: bool) -> PolarsResult<Column> {
1253 self.as_materialized_series()
1254 .explode(skip_empty)
1255 .map(Column::from)
1256 }
1257 pub fn implode(&self) -> PolarsResult<ListChunked> {
1258 self.as_materialized_series().implode()
1259 }
1260
1261 pub fn fill_null(&self, strategy: FillNullStrategy) -> PolarsResult<Self> {
1262 self.as_materialized_series()
1264 .fill_null(strategy)
1265 .map(Column::from)
1266 }
1267
1268 pub fn divide(&self, rhs: &Column) -> PolarsResult<Self> {
1269 self.as_materialized_series()
1271 .divide(rhs.as_materialized_series())
1272 .map(Column::from)
1273 }
1274
1275 pub fn shift(&self, periods: i64) -> Column {
1276 self.as_materialized_series().shift(periods).into()
1278 }
1279
1280 #[cfg(feature = "zip_with")]
1281 pub fn zip_with(&self, mask: &BooleanChunked, other: &Self) -> PolarsResult<Self> {
1282 self.as_materialized_series()
1284 .zip_with(mask, other.as_materialized_series())
1285 .map(Self::from)
1286 }
1287
1288 #[cfg(feature = "zip_with")]
1289 pub fn zip_with_same_type(
1290 &self,
1291 mask: &ChunkedArray<BooleanType>,
1292 other: &Column,
1293 ) -> PolarsResult<Column> {
1294 self.as_materialized_series()
1296 .zip_with_same_type(mask, other.as_materialized_series())
1297 .map(Column::from)
1298 }
1299
1300 pub fn drop_nulls(&self) -> Column {
1301 match self {
1302 Column::Series(s) => s.drop_nulls().into_column(),
1303 Column::Partitioned(s) => s.as_materialized_series().drop_nulls().into_column(),
1305 Column::Scalar(s) => s.drop_nulls().into_column(),
1306 }
1307 }
1308
1309 pub fn as_list(&self) -> ListChunked {
1311 self.as_materialized_series().as_list()
1314 }
1315
1316 pub fn is_sorted_flag(&self) -> IsSorted {
1317 match self {
1318 Column::Series(s) => s.is_sorted_flag(),
1319 Column::Partitioned(s) => s.partitions().is_sorted_flag(),
1320 Column::Scalar(_) => IsSorted::Ascending,
1321 }
1322 }
1323
1324 pub fn unique(&self) -> PolarsResult<Column> {
1325 match self {
1326 Column::Series(s) => s.unique().map(Column::from),
1327 Column::Partitioned(s) => s.as_materialized_series().unique().map(Column::from),
1329 Column::Scalar(s) => {
1330 _ = s.as_single_value_series().unique()?;
1331 if s.is_empty() {
1332 return Ok(s.clone().into_column());
1333 }
1334
1335 Ok(s.resize(1).into_column())
1336 },
1337 }
1338 }
1339 pub fn unique_stable(&self) -> PolarsResult<Column> {
1340 match self {
1341 Column::Series(s) => s.unique_stable().map(Column::from),
1342 Column::Partitioned(s) => s.as_materialized_series().unique_stable().map(Column::from),
1344 Column::Scalar(s) => {
1345 _ = s.as_single_value_series().unique_stable()?;
1346 if s.is_empty() {
1347 return Ok(s.clone().into_column());
1348 }
1349
1350 Ok(s.resize(1).into_column())
1351 },
1352 }
1353 }
1354
1355 pub fn reshape_list(&self, dimensions: &[ReshapeDimension]) -> PolarsResult<Self> {
1356 self.as_materialized_series()
1358 .reshape_list(dimensions)
1359 .map(Self::from)
1360 }
1361
1362 #[cfg(feature = "dtype-array")]
1363 pub fn reshape_array(&self, dimensions: &[ReshapeDimension]) -> PolarsResult<Self> {
1364 self.as_materialized_series()
1366 .reshape_array(dimensions)
1367 .map(Self::from)
1368 }
1369
1370 pub fn sort(&self, sort_options: SortOptions) -> PolarsResult<Self> {
1371 self.as_materialized_series()
1373 .sort(sort_options)
1374 .map(Self::from)
1375 }
1376
1377 pub fn filter(&self, filter: &BooleanChunked) -> PolarsResult<Self> {
1378 match self {
1379 Column::Series(s) => s.filter(filter).map(Column::from),
1380 Column::Partitioned(s) => s.as_materialized_series().filter(filter).map(Column::from),
1381 Column::Scalar(s) => {
1382 if s.is_empty() {
1383 return Ok(s.clone().into_column());
1384 }
1385
1386 if filter.len() == 1 {
1388 return match filter.get(0) {
1389 Some(true) => Ok(s.clone().into_column()),
1390 _ => Ok(s.resize(0).into_column()),
1391 };
1392 }
1393
1394 Ok(s.resize(filter.sum().unwrap() as usize).into_column())
1395 },
1396 }
1397 }
1398
1399 #[cfg(feature = "random")]
1400 pub fn shuffle(&self, seed: Option<u64>) -> Self {
1401 self.as_materialized_series().shuffle(seed).into()
1403 }
1404
1405 #[cfg(feature = "random")]
1406 pub fn sample_frac(
1407 &self,
1408 frac: f64,
1409 with_replacement: bool,
1410 shuffle: bool,
1411 seed: Option<u64>,
1412 ) -> PolarsResult<Self> {
1413 self.as_materialized_series()
1414 .sample_frac(frac, with_replacement, shuffle, seed)
1415 .map(Self::from)
1416 }
1417
1418 #[cfg(feature = "random")]
1419 pub fn sample_n(
1420 &self,
1421 n: usize,
1422 with_replacement: bool,
1423 shuffle: bool,
1424 seed: Option<u64>,
1425 ) -> PolarsResult<Self> {
1426 self.as_materialized_series()
1427 .sample_n(n, with_replacement, shuffle, seed)
1428 .map(Self::from)
1429 }
1430
1431 pub fn gather_every(&self, n: usize, offset: usize) -> PolarsResult<Column> {
1432 polars_ensure!(n > 0, InvalidOperation: "gather_every(n): n should be positive");
1433 if self.len().saturating_sub(offset) == 0 {
1434 return Ok(self.clear());
1435 }
1436
1437 match self {
1438 Column::Series(s) => Ok(s.gather_every(n, offset)?.into()),
1439 Column::Partitioned(s) => {
1440 Ok(s.as_materialized_series().gather_every(n, offset)?.into())
1441 },
1442 Column::Scalar(s) => {
1443 let total = s.len() - offset;
1444 Ok(s.resize(1 + (total - 1) / n).into())
1445 },
1446 }
1447 }
1448
1449 pub fn extend_constant(&self, value: AnyValue, n: usize) -> PolarsResult<Self> {
1450 if self.is_empty() {
1451 return Ok(Self::new_scalar(
1452 self.name().clone(),
1453 Scalar::new(self.dtype().clone(), value.into_static()),
1454 n,
1455 ));
1456 }
1457
1458 match self {
1459 Column::Series(s) => s.extend_constant(value, n).map(Column::from),
1460 Column::Partitioned(s) => s.extend_constant(value, n).map(Column::from),
1461 Column::Scalar(s) => {
1462 if s.scalar().as_any_value() == value {
1463 Ok(s.resize(s.len() + n).into())
1464 } else {
1465 s.as_materialized_series()
1466 .extend_constant(value, n)
1467 .map(Column::from)
1468 }
1469 },
1470 }
1471 }
1472
1473 pub fn is_finite(&self) -> PolarsResult<BooleanChunked> {
1474 self.try_map_unary_elementwise_to_bool(|s| s.is_finite())
1475 }
1476 pub fn is_infinite(&self) -> PolarsResult<BooleanChunked> {
1477 self.try_map_unary_elementwise_to_bool(|s| s.is_infinite())
1478 }
1479 pub fn is_nan(&self) -> PolarsResult<BooleanChunked> {
1480 self.try_map_unary_elementwise_to_bool(|s| s.is_nan())
1481 }
1482 pub fn is_not_nan(&self) -> PolarsResult<BooleanChunked> {
1483 self.try_map_unary_elementwise_to_bool(|s| s.is_not_nan())
1484 }
1485
1486 pub fn wrapping_trunc_div_scalar<T>(&self, rhs: T) -> Self
1487 where
1488 T: Num + NumCast,
1489 {
1490 self.as_materialized_series()
1492 .wrapping_trunc_div_scalar(rhs)
1493 .into()
1494 }
1495
1496 pub fn product(&self) -> PolarsResult<Scalar> {
1497 self.as_materialized_series().product()
1499 }
1500
1501 pub fn phys_iter(&self) -> SeriesPhysIter<'_> {
1502 self.as_materialized_series().phys_iter()
1504 }
1505
1506 #[inline]
1507 pub fn get(&self, index: usize) -> PolarsResult<AnyValue<'_>> {
1508 polars_ensure!(index < self.len(), oob = index, self.len());
1509
1510 Ok(unsafe { self.get_unchecked(index) })
1512 }
1513 #[inline(always)]
1517 pub unsafe fn get_unchecked(&self, index: usize) -> AnyValue<'_> {
1518 debug_assert!(index < self.len());
1519
1520 match self {
1521 Column::Series(s) => unsafe { s.get_unchecked(index) },
1522 Column::Partitioned(s) => unsafe { s.get_unchecked(index) },
1523 Column::Scalar(s) => s.scalar().as_any_value(),
1524 }
1525 }
1526
1527 #[cfg(feature = "object")]
1528 pub fn get_object(
1529 &self,
1530 index: usize,
1531 ) -> Option<&dyn crate::chunked_array::object::PolarsObjectSafe> {
1532 self.as_materialized_series().get_object(index)
1533 }
1534
1535 pub fn bitand(&self, rhs: &Self) -> PolarsResult<Self> {
1536 self.try_apply_broadcasting_binary_elementwise(rhs, |l, r| l & r)
1537 }
1538 pub fn bitor(&self, rhs: &Self) -> PolarsResult<Self> {
1539 self.try_apply_broadcasting_binary_elementwise(rhs, |l, r| l | r)
1540 }
1541 pub fn bitxor(&self, rhs: &Self) -> PolarsResult<Self> {
1542 self.try_apply_broadcasting_binary_elementwise(rhs, |l, r| l ^ r)
1543 }
1544
1545 pub fn try_add_owned(self, other: Self) -> PolarsResult<Self> {
1546 match (self, other) {
1547 (Column::Series(lhs), Column::Series(rhs)) => {
1548 lhs.take().try_add_owned(rhs.take()).map(Column::from)
1549 },
1550 (lhs, rhs) => lhs + rhs,
1551 }
1552 }
1553 pub fn try_sub_owned(self, other: Self) -> PolarsResult<Self> {
1554 match (self, other) {
1555 (Column::Series(lhs), Column::Series(rhs)) => {
1556 lhs.take().try_sub_owned(rhs.take()).map(Column::from)
1557 },
1558 (lhs, rhs) => lhs - rhs,
1559 }
1560 }
1561 pub fn try_mul_owned(self, other: Self) -> PolarsResult<Self> {
1562 match (self, other) {
1563 (Column::Series(lhs), Column::Series(rhs)) => {
1564 lhs.take().try_mul_owned(rhs.take()).map(Column::from)
1565 },
1566 (lhs, rhs) => lhs * rhs,
1567 }
1568 }
1569
1570 pub(crate) fn str_value(&self, index: usize) -> PolarsResult<Cow<'_, str>> {
1571 Ok(self.get(index)?.str_value())
1572 }
1573
1574 pub fn min_reduce(&self) -> PolarsResult<Scalar> {
1575 match self {
1576 Column::Series(s) => s.min_reduce(),
1577 Column::Partitioned(s) => s.min_reduce(),
1578 Column::Scalar(s) => {
1579 s.as_single_value_series().min_reduce()
1582 },
1583 }
1584 }
1585 pub fn max_reduce(&self) -> PolarsResult<Scalar> {
1586 match self {
1587 Column::Series(s) => s.max_reduce(),
1588 Column::Partitioned(s) => s.max_reduce(),
1589 Column::Scalar(s) => {
1590 s.as_single_value_series().max_reduce()
1593 },
1594 }
1595 }
1596 pub fn median_reduce(&self) -> PolarsResult<Scalar> {
1597 match self {
1598 Column::Series(s) => s.median_reduce(),
1599 Column::Partitioned(s) => s.as_materialized_series().median_reduce(),
1600 Column::Scalar(s) => {
1601 s.as_single_value_series().median_reduce()
1604 },
1605 }
1606 }
1607 pub fn mean_reduce(&self) -> Scalar {
1608 match self {
1609 Column::Series(s) => s.mean_reduce(),
1610 Column::Partitioned(s) => s.as_materialized_series().mean_reduce(),
1611 Column::Scalar(s) => {
1612 s.as_single_value_series().mean_reduce()
1615 },
1616 }
1617 }
1618 pub fn std_reduce(&self, ddof: u8) -> PolarsResult<Scalar> {
1619 match self {
1620 Column::Series(s) => s.std_reduce(ddof),
1621 Column::Partitioned(s) => s.as_materialized_series().std_reduce(ddof),
1622 Column::Scalar(s) => {
1623 let n = s.len().min(ddof as usize + 1);
1626 s.as_n_values_series(n).std_reduce(ddof)
1627 },
1628 }
1629 }
1630 pub fn var_reduce(&self, ddof: u8) -> PolarsResult<Scalar> {
1631 match self {
1632 Column::Series(s) => s.var_reduce(ddof),
1633 Column::Partitioned(s) => s.as_materialized_series().var_reduce(ddof),
1634 Column::Scalar(s) => {
1635 let n = s.len().min(ddof as usize + 1);
1638 s.as_n_values_series(n).var_reduce(ddof)
1639 },
1640 }
1641 }
1642 pub fn sum_reduce(&self) -> PolarsResult<Scalar> {
1643 self.as_materialized_series().sum_reduce()
1646 }
1647 pub fn and_reduce(&self) -> PolarsResult<Scalar> {
1648 match self {
1649 Column::Series(s) => s.and_reduce(),
1650 Column::Partitioned(s) => s.and_reduce(),
1651 Column::Scalar(s) => {
1652 s.as_single_value_series().and_reduce()
1655 },
1656 }
1657 }
1658 pub fn or_reduce(&self) -> PolarsResult<Scalar> {
1659 match self {
1660 Column::Series(s) => s.or_reduce(),
1661 Column::Partitioned(s) => s.or_reduce(),
1662 Column::Scalar(s) => {
1663 s.as_single_value_series().or_reduce()
1666 },
1667 }
1668 }
1669 pub fn xor_reduce(&self) -> PolarsResult<Scalar> {
1670 match self {
1671 Column::Series(s) => s.xor_reduce(),
1672 Column::Partitioned(s) => s.as_materialized_series().xor_reduce(),
1674 Column::Scalar(s) => {
1675 s.as_n_values_series(2 - s.len() % 2).xor_reduce()
1682 },
1683 }
1684 }
1685 pub fn n_unique(&self) -> PolarsResult<usize> {
1686 match self {
1687 Column::Series(s) => s.n_unique(),
1688 Column::Partitioned(s) => s.partitions().n_unique(),
1689 Column::Scalar(s) => s.as_single_value_series().n_unique(),
1690 }
1691 }
1692 pub fn quantile_reduce(&self, quantile: f64, method: QuantileMethod) -> PolarsResult<Scalar> {
1693 self.as_materialized_series()
1694 .quantile_reduce(quantile, method)
1695 }
1696
1697 pub(crate) fn estimated_size(&self) -> usize {
1698 self.as_materialized_series().estimated_size()
1700 }
1701
1702 pub fn sort_with(&self, options: SortOptions) -> PolarsResult<Self> {
1703 match self {
1704 Column::Series(s) => s.sort_with(options).map(Self::from),
1705 Column::Partitioned(s) => s
1707 .as_materialized_series()
1708 .sort_with(options)
1709 .map(Self::from),
1710 Column::Scalar(s) => {
1711 _ = s.as_single_value_series().sort_with(options)?;
1713
1714 Ok(self.clone())
1715 },
1716 }
1717 }
1718
1719 pub fn map_unary_elementwise_to_bool(
1720 &self,
1721 f: impl Fn(&Series) -> BooleanChunked,
1722 ) -> BooleanChunked {
1723 self.try_map_unary_elementwise_to_bool(|s| Ok(f(s)))
1724 .unwrap()
1725 }
1726 pub fn try_map_unary_elementwise_to_bool(
1727 &self,
1728 f: impl Fn(&Series) -> PolarsResult<BooleanChunked>,
1729 ) -> PolarsResult<BooleanChunked> {
1730 match self {
1731 Column::Series(s) => f(s),
1732 Column::Partitioned(s) => f(s.as_materialized_series()),
1733 Column::Scalar(s) => Ok(f(&s.as_single_value_series())?.new_from_index(0, s.len())),
1734 }
1735 }
1736
1737 pub fn apply_unary_elementwise(&self, f: impl Fn(&Series) -> Series) -> Column {
1738 self.try_apply_unary_elementwise(|s| Ok(f(s))).unwrap()
1739 }
1740 pub fn try_apply_unary_elementwise(
1741 &self,
1742 f: impl Fn(&Series) -> PolarsResult<Series>,
1743 ) -> PolarsResult<Column> {
1744 match self {
1745 Column::Series(s) => f(s).map(Column::from),
1746 Column::Partitioned(s) => s.try_apply_unary_elementwise(f).map(Self::from),
1747 Column::Scalar(s) => Ok(ScalarColumn::from_single_value_series(
1748 f(&s.as_single_value_series())?,
1749 s.len(),
1750 )
1751 .into()),
1752 }
1753 }
1754
1755 pub fn apply_broadcasting_binary_elementwise(
1756 &self,
1757 other: &Self,
1758 op: impl Fn(&Series, &Series) -> Series,
1759 ) -> PolarsResult<Column> {
1760 self.try_apply_broadcasting_binary_elementwise(other, |lhs, rhs| Ok(op(lhs, rhs)))
1761 }
1762 pub fn try_apply_broadcasting_binary_elementwise(
1763 &self,
1764 other: &Self,
1765 op: impl Fn(&Series, &Series) -> PolarsResult<Series>,
1766 ) -> PolarsResult<Column> {
1767 fn output_length(a: &Column, b: &Column) -> PolarsResult<usize> {
1768 match (a.len(), b.len()) {
1769 (1, o) | (o, 1) => Ok(o),
1771 (a, b) if a == b => Ok(a),
1773 (a, b) => {
1775 polars_bail!(InvalidOperation: "cannot do a binary operation on columns of different lengths: got {} and {}", a, b)
1776 },
1777 }
1778 }
1779
1780 let length = output_length(self, other)?;
1782 match (self, other) {
1783 (Column::Series(lhs), Column::Series(rhs)) => op(lhs, rhs).map(Column::from),
1784 (Column::Series(lhs), Column::Scalar(rhs)) => {
1785 op(lhs, &rhs.as_single_value_series()).map(Column::from)
1786 },
1787 (Column::Scalar(lhs), Column::Series(rhs)) => {
1788 op(&lhs.as_single_value_series(), rhs).map(Column::from)
1789 },
1790 (Column::Scalar(lhs), Column::Scalar(rhs)) => {
1791 let lhs = lhs.as_single_value_series();
1792 let rhs = rhs.as_single_value_series();
1793
1794 Ok(ScalarColumn::from_single_value_series(op(&lhs, &rhs)?, length).into_column())
1795 },
1796 (lhs, rhs) => {
1798 op(lhs.as_materialized_series(), rhs.as_materialized_series()).map(Column::from)
1799 },
1800 }
1801 }
1802
1803 pub fn apply_binary_elementwise(
1804 &self,
1805 other: &Self,
1806 f: impl Fn(&Series, &Series) -> Series,
1807 f_lb: impl Fn(&Scalar, &Series) -> Series,
1808 f_rb: impl Fn(&Series, &Scalar) -> Series,
1809 ) -> Column {
1810 self.try_apply_binary_elementwise(
1811 other,
1812 |lhs, rhs| Ok(f(lhs, rhs)),
1813 |lhs, rhs| Ok(f_lb(lhs, rhs)),
1814 |lhs, rhs| Ok(f_rb(lhs, rhs)),
1815 )
1816 .unwrap()
1817 }
1818 pub fn try_apply_binary_elementwise(
1819 &self,
1820 other: &Self,
1821 f: impl Fn(&Series, &Series) -> PolarsResult<Series>,
1822 f_lb: impl Fn(&Scalar, &Series) -> PolarsResult<Series>,
1823 f_rb: impl Fn(&Series, &Scalar) -> PolarsResult<Series>,
1824 ) -> PolarsResult<Column> {
1825 debug_assert_eq!(self.len(), other.len());
1826
1827 match (self, other) {
1828 (Column::Series(lhs), Column::Series(rhs)) => f(lhs, rhs).map(Column::from),
1829 (Column::Series(lhs), Column::Scalar(rhs)) => f_rb(lhs, rhs.scalar()).map(Column::from),
1830 (Column::Scalar(lhs), Column::Series(rhs)) => f_lb(lhs.scalar(), rhs).map(Column::from),
1831 (Column::Scalar(lhs), Column::Scalar(rhs)) => {
1832 let lhs = lhs.as_single_value_series();
1833 let rhs = rhs.as_single_value_series();
1834
1835 Ok(
1836 ScalarColumn::from_single_value_series(f(&lhs, &rhs)?, self.len())
1837 .into_column(),
1838 )
1839 },
1840 (lhs, rhs) => {
1842 f(lhs.as_materialized_series(), rhs.as_materialized_series()).map(Column::from)
1843 },
1844 }
1845 }
1846
1847 #[cfg(feature = "approx_unique")]
1848 pub fn approx_n_unique(&self) -> PolarsResult<IdxSize> {
1849 match self {
1850 Column::Series(s) => s.approx_n_unique(),
1851 Column::Partitioned(s) => s.as_materialized_series().approx_n_unique(),
1853 Column::Scalar(s) => {
1854 s.as_single_value_series().approx_n_unique()?;
1856 Ok(1)
1857 },
1858 }
1859 }
1860
1861 pub fn n_chunks(&self) -> usize {
1862 match self {
1863 Column::Series(s) => s.n_chunks(),
1864 Column::Scalar(s) => s.lazy_as_materialized_series().map_or(1, |x| x.n_chunks()),
1865 Column::Partitioned(s) => {
1866 if let Some(s) = s.lazy_as_materialized_series() {
1867 debug_assert_eq!(s.n_chunks(), 1)
1869 }
1870 1
1871 },
1872 }
1873 }
1874
1875 #[expect(clippy::wrong_self_convention)]
1876 pub(crate) fn into_total_ord_inner<'a>(&'a self) -> Box<dyn TotalOrdInner + 'a> {
1877 self.as_materialized_series().into_total_ord_inner()
1879 }
1880 #[expect(unused, clippy::wrong_self_convention)]
1881 pub(crate) fn into_total_eq_inner<'a>(&'a self) -> Box<dyn TotalEqInner + 'a> {
1882 self.as_materialized_series().into_total_eq_inner()
1884 }
1885
1886 pub fn rechunk_to_arrow(self, compat_level: CompatLevel) -> Box<dyn Array> {
1887 let mut series = self.take_materialized_series();
1889 if series.n_chunks() > 1 {
1890 series = series.rechunk();
1891 }
1892 series.to_arrow(0, compat_level)
1893 }
1894
1895 pub fn trim_lists_to_normalized_offsets(&self) -> Option<Column> {
1896 self.as_materialized_series()
1897 .trim_lists_to_normalized_offsets()
1898 .map(Column::from)
1899 }
1900
1901 pub fn propagate_nulls(&self) -> Option<Column> {
1902 self.as_materialized_series()
1903 .propagate_nulls()
1904 .map(Column::from)
1905 }
1906}
1907
1908impl Default for Column {
1909 fn default() -> Self {
1910 Self::new_scalar(
1911 PlSmallStr::EMPTY,
1912 Scalar::new(DataType::Int64, AnyValue::Null),
1913 0,
1914 )
1915 }
1916}
1917
1918impl PartialEq for Column {
1919 fn eq(&self, other: &Self) -> bool {
1920 self.as_materialized_series()
1922 .eq(other.as_materialized_series())
1923 }
1924}
1925
1926impl From<Series> for Column {
1927 #[inline]
1928 fn from(series: Series) -> Self {
1929 if series.len() == 1 {
1932 return Self::Scalar(ScalarColumn::unit_scalar_from_series(series));
1933 }
1934
1935 Self::Series(SeriesColumn::new(series))
1936 }
1937}
1938
1939impl<T: IntoSeries> IntoColumn for T {
1940 #[inline]
1941 fn into_column(self) -> Column {
1942 self.into_series().into()
1943 }
1944}
1945
1946impl IntoColumn for Column {
1947 #[inline(always)]
1948 fn into_column(self) -> Column {
1949 self
1950 }
1951}
1952
1953#[derive(Clone)]
1958#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1959#[cfg_attr(feature = "serde", serde(into = "Series"))]
1960struct _SerdeSeries(Series);
1961
1962impl From<Column> for _SerdeSeries {
1963 #[inline]
1964 fn from(value: Column) -> Self {
1965 Self(value.take_materialized_series())
1966 }
1967}
1968
1969impl From<_SerdeSeries> for Series {
1970 #[inline]
1971 fn from(value: _SerdeSeries) -> Self {
1972 value.0
1973 }
1974}