1use std::borrow::Cow;
2
3use arrow::bitmap::{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 #[must_use]
536 pub fn clear(&self) -> Self {
537 match self {
538 Column::Series(s) => s.clear().into(),
539 Column::Partitioned(s) => s.clear().into(),
540 Column::Scalar(s) => s.resize(0).into(),
541 }
542 }
543
544 #[inline]
545 pub fn shrink_to_fit(&mut self) {
546 match self {
547 Column::Series(s) => s.shrink_to_fit(),
548 Column::Partitioned(_) => {},
550 Column::Scalar(_) => {},
551 }
552 }
553
554 #[inline]
555 pub fn new_from_index(&self, index: usize, length: usize) -> Self {
556 if index >= self.len() {
557 return Self::full_null(self.name().clone(), length, self.dtype());
558 }
559
560 match self {
561 Column::Series(s) => {
562 let av = unsafe { s.get_unchecked(index) };
564 let scalar = Scalar::new(self.dtype().clone(), av.into_static());
565 Self::new_scalar(self.name().clone(), scalar, length)
566 },
567 Column::Partitioned(s) => {
568 let av = unsafe { s.get_unchecked(index) };
570 let scalar = Scalar::new(self.dtype().clone(), av.into_static());
571 Self::new_scalar(self.name().clone(), scalar, length)
572 },
573 Column::Scalar(s) => s.resize(length).into(),
574 }
575 }
576
577 #[inline]
578 pub fn has_nulls(&self) -> bool {
579 match self {
580 Self::Series(s) => s.has_nulls(),
581 Self::Partitioned(s) => s.as_materialized_series().has_nulls(),
583 Self::Scalar(s) => s.has_nulls(),
584 }
585 }
586
587 #[inline]
588 pub fn is_null(&self) -> BooleanChunked {
589 match self {
590 Self::Series(s) => s.is_null(),
591 Self::Partitioned(s) => s.as_materialized_series().is_null(),
593 Self::Scalar(s) => {
594 BooleanChunked::full(s.name().clone(), s.scalar().is_null(), s.len())
595 },
596 }
597 }
598 #[inline]
599 pub fn is_not_null(&self) -> BooleanChunked {
600 match self {
601 Self::Series(s) => s.is_not_null(),
602 Self::Partitioned(s) => s.as_materialized_series().is_not_null(),
604 Self::Scalar(s) => {
605 BooleanChunked::full(s.name().clone(), !s.scalar().is_null(), s.len())
606 },
607 }
608 }
609
610 pub fn to_physical_repr(&self) -> Column {
611 self.as_materialized_series()
613 .to_physical_repr()
614 .into_owned()
615 .into()
616 }
617 pub unsafe fn from_physical_unchecked(&self, dtype: &DataType) -> PolarsResult<Column> {
621 self.as_materialized_series()
623 .from_physical_unchecked(dtype)
624 .map(Column::from)
625 }
626
627 pub fn head(&self, length: Option<usize>) -> Column {
628 let len = length.unwrap_or(HEAD_DEFAULT_LENGTH);
629 let len = usize::min(len, self.len());
630 self.slice(0, len)
631 }
632 pub fn tail(&self, length: Option<usize>) -> Column {
633 let len = length.unwrap_or(TAIL_DEFAULT_LENGTH);
634 let len = usize::min(len, self.len());
635 debug_assert!(len <= i64::MAX as usize);
636 self.slice(-(len as i64), len)
637 }
638 pub fn slice(&self, offset: i64, length: usize) -> Column {
639 match self {
640 Column::Series(s) => s.slice(offset, length).into(),
641 Column::Partitioned(s) => s.as_materialized_series().slice(offset, length).into(),
643 Column::Scalar(s) => {
644 let (_, length) = slice_offsets(offset, length, s.len());
645 s.resize(length).into()
646 },
647 }
648 }
649
650 pub fn split_at(&self, offset: i64) -> (Column, Column) {
651 let (l, r) = self.as_materialized_series().split_at(offset);
653 (l.into(), r.into())
654 }
655
656 #[inline]
657 pub fn null_count(&self) -> usize {
658 match self {
659 Self::Series(s) => s.null_count(),
660 Self::Partitioned(s) => s.null_count(),
661 Self::Scalar(s) if s.scalar().is_null() => s.len(),
662 Self::Scalar(_) => 0,
663 }
664 }
665
666 pub fn take(&self, indices: &IdxCa) -> PolarsResult<Column> {
667 check_bounds_ca(indices, self.len() as IdxSize)?;
668 Ok(unsafe { self.take_unchecked(indices) })
669 }
670 pub fn take_slice(&self, indices: &[IdxSize]) -> PolarsResult<Column> {
671 check_bounds(indices, self.len() as IdxSize)?;
672 Ok(unsafe { self.take_slice_unchecked(indices) })
673 }
674 pub unsafe fn take_unchecked(&self, indices: &IdxCa) -> Column {
678 debug_assert!(check_bounds_ca(indices, self.len() as IdxSize).is_ok());
679
680 match self {
681 Self::Series(s) => unsafe { s.take_unchecked(indices) }.into(),
682 Self::Partitioned(s) => {
683 let s = s.as_materialized_series();
684 unsafe { s.take_unchecked(indices) }.into()
685 },
686 Self::Scalar(s) => {
687 let idxs_length = indices.len();
688 let idxs_null_count = indices.null_count();
689
690 let scalar = ScalarColumn::from_single_value_series(
691 s.as_single_value_series().take_unchecked(&IdxCa::new(
692 indices.name().clone(),
693 &[0][..s.len().min(1)],
694 )),
695 idxs_length,
696 );
697
698 if idxs_null_count == 0 || scalar.has_nulls() {
700 scalar.into_column()
701 } else if idxs_null_count == idxs_length {
702 scalar.into_nulls().into_column()
703 } else {
704 let validity = indices.rechunk_validity();
705 let series = scalar.take_materialized_series();
706 let name = series.name().clone();
707 let dtype = series.dtype().clone();
708 let mut chunks = series.into_chunks();
709 assert_eq!(chunks.len(), 1);
710 chunks[0] = chunks[0].with_validity(validity);
711 unsafe { Series::from_chunks_and_dtype_unchecked(name, chunks, &dtype) }
712 .into_column()
713 }
714 },
715 }
716 }
717 pub unsafe fn take_slice_unchecked(&self, indices: &[IdxSize]) -> Column {
721 debug_assert!(check_bounds(indices, self.len() as IdxSize).is_ok());
722
723 match self {
724 Self::Series(s) => unsafe { s.take_slice_unchecked(indices) }.into(),
725 Self::Partitioned(s) => {
726 let s = s.as_materialized_series();
727 unsafe { s.take_slice_unchecked(indices) }.into()
728 },
729 Self::Scalar(s) => ScalarColumn::from_single_value_series(
730 s.as_single_value_series()
731 .take_slice_unchecked(&[0][..s.len().min(1)]),
732 indices.len(),
733 )
734 .into(),
735 }
736 }
737
738 #[inline(always)]
740 #[cfg(any(feature = "algorithm_group_by", feature = "bitwise"))]
741 fn agg_with_unit_scalar(
742 &self,
743 groups: &GroupsType,
744 series_agg: impl Fn(&Series, &GroupsType) -> Series,
745 ) -> Column {
746 match self {
747 Column::Series(s) => series_agg(s, groups).into_column(),
748 Column::Partitioned(s) => series_agg(s.as_materialized_series(), groups).into_column(),
750 Column::Scalar(s) => {
751 if s.is_empty() {
752 return series_agg(s.as_materialized_series(), groups).into_column();
753 }
754
755 let series_aggregation = series_agg(
759 &s.as_single_value_series(),
760 &GroupsType::Slice {
761 groups: vec![[0, 1]],
763 overlapping: false,
764 },
765 );
766
767 if series_aggregation.has_nulls() {
769 return Self::new_scalar(
770 series_aggregation.name().clone(),
771 Scalar::new(series_aggregation.dtype().clone(), AnyValue::Null),
772 groups.len(),
773 );
774 }
775
776 let mut scalar_col = s.resize(groups.len());
777 if series_aggregation.dtype() != s.dtype() {
780 scalar_col = scalar_col.cast(series_aggregation.dtype()).unwrap();
781 }
782
783 let Some(first_empty_idx) = groups.iter().position(|g| g.is_empty()) else {
784 return scalar_col.into_column();
786 };
787
788 let mut validity = BitmapBuilder::with_capacity(groups.len());
790 validity.extend_constant(first_empty_idx, true);
791 let iter = unsafe {
793 TrustMyLength::new(
794 groups.iter().skip(first_empty_idx).map(|g| !g.is_empty()),
795 groups.len() - first_empty_idx,
796 )
797 };
798 validity.extend_trusted_len_iter(iter);
799
800 let mut s = scalar_col.take_materialized_series().rechunk();
801 let chunks = unsafe { s.chunks_mut() };
803 let arr = &mut chunks[0];
804 *arr = arr.with_validity(validity.into_opt_validity());
805 s.compute_len();
806
807 s.into_column()
808 },
809 }
810 }
811
812 #[cfg(feature = "algorithm_group_by")]
816 pub unsafe fn agg_min(&self, groups: &GroupsType) -> Self {
817 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_min(g) })
818 }
819
820 #[cfg(feature = "algorithm_group_by")]
824 pub unsafe fn agg_max(&self, groups: &GroupsType) -> Self {
825 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_max(g) })
826 }
827
828 #[cfg(feature = "algorithm_group_by")]
832 pub unsafe fn agg_mean(&self, groups: &GroupsType) -> Self {
833 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_mean(g) })
834 }
835
836 #[cfg(feature = "algorithm_group_by")]
840 pub unsafe fn agg_sum(&self, groups: &GroupsType) -> Self {
841 unsafe { self.as_materialized_series().agg_sum(groups) }.into()
843 }
844
845 #[cfg(feature = "algorithm_group_by")]
849 pub unsafe fn agg_first(&self, groups: &GroupsType) -> Self {
850 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_first(g) })
851 }
852
853 #[cfg(feature = "algorithm_group_by")]
857 pub unsafe fn agg_last(&self, groups: &GroupsType) -> Self {
858 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_last(g) })
859 }
860
861 #[cfg(feature = "algorithm_group_by")]
865 pub unsafe fn agg_n_unique(&self, groups: &GroupsType) -> Self {
866 unsafe { self.as_materialized_series().agg_n_unique(groups) }.into()
868 }
869
870 #[cfg(feature = "algorithm_group_by")]
874 pub unsafe fn agg_quantile(
875 &self,
876 groups: &GroupsType,
877 quantile: f64,
878 method: QuantileMethod,
879 ) -> Self {
880 unsafe {
883 self.as_materialized_series()
884 .agg_quantile(groups, quantile, method)
885 }
886 .into()
887 }
888
889 #[cfg(feature = "algorithm_group_by")]
893 pub unsafe fn agg_median(&self, groups: &GroupsType) -> Self {
894 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_median(g) })
895 }
896
897 #[cfg(feature = "algorithm_group_by")]
901 pub unsafe fn agg_var(&self, groups: &GroupsType, ddof: u8) -> Self {
902 unsafe { self.as_materialized_series().agg_var(groups, ddof) }.into()
904 }
905
906 #[cfg(feature = "algorithm_group_by")]
910 pub unsafe fn agg_std(&self, groups: &GroupsType, ddof: u8) -> Self {
911 unsafe { self.as_materialized_series().agg_std(groups, ddof) }.into()
913 }
914
915 #[cfg(feature = "algorithm_group_by")]
919 pub unsafe fn agg_list(&self, groups: &GroupsType) -> Self {
920 unsafe { self.as_materialized_series().agg_list(groups) }.into()
922 }
923
924 #[cfg(feature = "algorithm_group_by")]
928 pub fn agg_valid_count(&self, groups: &GroupsType) -> Self {
929 unsafe { self.as_materialized_series().agg_valid_count(groups) }.into()
932 }
933
934 #[cfg(feature = "bitwise")]
938 pub unsafe fn agg_and(&self, groups: &GroupsType) -> Self {
939 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_and(g) })
940 }
941 #[cfg(feature = "bitwise")]
945 pub unsafe fn agg_or(&self, groups: &GroupsType) -> Self {
946 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_or(g) })
947 }
948 #[cfg(feature = "bitwise")]
952 pub unsafe fn agg_xor(&self, groups: &GroupsType) -> Self {
953 unsafe { self.as_materialized_series().agg_xor(groups) }.into()
956 }
957
958 pub fn full_null(name: PlSmallStr, size: usize, dtype: &DataType) -> Self {
959 Self::new_scalar(name, Scalar::new(dtype.clone(), AnyValue::Null), size)
960 }
961
962 pub fn is_empty(&self) -> bool {
963 self.len() == 0
964 }
965
966 pub fn reverse(&self) -> Column {
967 match self {
968 Column::Series(s) => s.reverse().into(),
969 Column::Partitioned(s) => s.reverse().into(),
970 Column::Scalar(_) => self.clone(),
971 }
972 }
973
974 pub fn equals(&self, other: &Column) -> bool {
975 self.as_materialized_series()
977 .equals(other.as_materialized_series())
978 }
979
980 pub fn equals_missing(&self, other: &Column) -> bool {
981 self.as_materialized_series()
983 .equals_missing(other.as_materialized_series())
984 }
985
986 pub fn set_sorted_flag(&mut self, sorted: IsSorted) {
987 match self {
989 Column::Series(s) => s.set_sorted_flag(sorted),
990 Column::Partitioned(s) => s.set_sorted_flag(sorted),
991 Column::Scalar(_) => {},
992 }
993 }
994
995 pub fn get_flags(&self) -> StatisticsFlags {
996 match self {
997 Column::Series(s) => s.get_flags(),
998 Column::Partitioned(_) => StatisticsFlags::empty(),
1000 Column::Scalar(_) => {
1001 StatisticsFlags::IS_SORTED_ASC | StatisticsFlags::CAN_FAST_EXPLODE_LIST
1002 },
1003 }
1004 }
1005
1006 pub fn set_flags(&mut self, flags: StatisticsFlags) -> bool {
1008 match self {
1009 Column::Series(s) => {
1010 s.set_flags(flags);
1011 true
1012 },
1013 Column::Partitioned(_) => false,
1015 Column::Scalar(_) => false,
1016 }
1017 }
1018
1019 pub fn vec_hash(
1020 &self,
1021 build_hasher: PlSeedableRandomStateQuality,
1022 buf: &mut Vec<u64>,
1023 ) -> PolarsResult<()> {
1024 self.as_materialized_series().vec_hash(build_hasher, buf)
1026 }
1027
1028 pub fn vec_hash_combine(
1029 &self,
1030 build_hasher: PlSeedableRandomStateQuality,
1031 hashes: &mut [u64],
1032 ) -> PolarsResult<()> {
1033 self.as_materialized_series()
1035 .vec_hash_combine(build_hasher, hashes)
1036 }
1037
1038 pub fn append(&mut self, other: &Column) -> PolarsResult<&mut Self> {
1039 self.into_materialized_series()
1041 .append(other.as_materialized_series())?;
1042 Ok(self)
1043 }
1044 pub fn append_owned(&mut self, other: Column) -> PolarsResult<&mut Self> {
1045 self.into_materialized_series()
1046 .append_owned(other.take_materialized_series())?;
1047 Ok(self)
1048 }
1049
1050 pub fn arg_sort(&self, options: SortOptions) -> IdxCa {
1051 if self.is_empty() {
1052 return IdxCa::from_vec(self.name().clone(), Vec::new());
1053 }
1054
1055 if self.null_count() == self.len() {
1056 let values = if options.descending {
1058 (0..self.len() as IdxSize).rev().collect()
1059 } else {
1060 (0..self.len() as IdxSize).collect()
1061 };
1062
1063 return IdxCa::from_vec(self.name().clone(), values);
1064 }
1065
1066 let is_sorted = Some(self.is_sorted_flag());
1067 let Some(is_sorted) = is_sorted.filter(|v| !matches!(v, IsSorted::Not)) else {
1068 return self.as_materialized_series().arg_sort(options);
1069 };
1070
1071 let is_sorted_dsc = matches!(is_sorted, IsSorted::Descending);
1073 let invert = options.descending != is_sorted_dsc;
1074
1075 let mut values = Vec::with_capacity(self.len());
1076
1077 #[inline(never)]
1078 fn extend(
1079 start: IdxSize,
1080 end: IdxSize,
1081 slf: &Column,
1082 values: &mut Vec<IdxSize>,
1083 is_only_nulls: bool,
1084 invert: bool,
1085 maintain_order: bool,
1086 ) {
1087 debug_assert!(start <= end);
1088 debug_assert!(start as usize <= slf.len());
1089 debug_assert!(end as usize <= slf.len());
1090
1091 if !invert || is_only_nulls {
1092 values.extend(start..end);
1093 return;
1094 }
1095
1096 if !maintain_order {
1098 values.extend((start..end).rev());
1099 return;
1100 }
1101
1102 let arg_unique = slf
1108 .slice(start as i64, (end - start) as usize)
1109 .arg_unique()
1110 .unwrap();
1111
1112 assert!(!arg_unique.has_nulls());
1113
1114 let num_unique = arg_unique.len();
1115
1116 if num_unique == (end - start) as usize {
1118 values.extend((start..end).rev());
1119 return;
1120 }
1121
1122 if num_unique == 1 {
1123 values.extend(start..end);
1124 return;
1125 }
1126
1127 let mut prev_idx = end - start;
1128 for chunk in arg_unique.downcast_iter() {
1129 for &idx in chunk.values().as_slice().iter().rev() {
1130 values.extend(start + idx..start + prev_idx);
1131 prev_idx = idx;
1132 }
1133 }
1134 }
1135 macro_rules! extend {
1136 ($start:expr, $end:expr) => {
1137 extend!($start, $end, is_only_nulls = false);
1138 };
1139 ($start:expr, $end:expr, is_only_nulls = $is_only_nulls:expr) => {
1140 extend(
1141 $start,
1142 $end,
1143 self,
1144 &mut values,
1145 $is_only_nulls,
1146 invert,
1147 options.maintain_order,
1148 );
1149 };
1150 }
1151
1152 let length = self.len() as IdxSize;
1153 let null_count = self.null_count() as IdxSize;
1154
1155 if null_count == 0 {
1156 extend!(0, length);
1157 } else {
1158 let has_nulls_last = self.get(self.len() - 1).unwrap().is_null();
1159 match (options.nulls_last, has_nulls_last) {
1160 (true, true) => {
1161 extend!(0, length - null_count);
1163 extend!(length - null_count, length, is_only_nulls = true);
1164 },
1165 (true, false) => {
1166 extend!(null_count, length);
1168 extend!(0, null_count, is_only_nulls = true);
1169 },
1170 (false, true) => {
1171 extend!(length - null_count, length, is_only_nulls = true);
1173 extend!(0, length - null_count);
1174 },
1175 (false, false) => {
1176 extend!(0, null_count, is_only_nulls = true);
1178 extend!(null_count, length);
1179 },
1180 }
1181 }
1182
1183 if let Some(limit) = options.limit {
1186 let limit = limit.min(length);
1187 values.truncate(limit as usize);
1188 }
1189
1190 IdxCa::from_vec(self.name().clone(), values)
1191 }
1192
1193 pub fn arg_sort_multiple(
1194 &self,
1195 by: &[Column],
1196 options: &SortMultipleOptions,
1197 ) -> PolarsResult<IdxCa> {
1198 self.as_materialized_series().arg_sort_multiple(by, options)
1200 }
1201
1202 pub fn arg_unique(&self) -> PolarsResult<IdxCa> {
1203 match self {
1204 Column::Scalar(s) => Ok(IdxCa::new_vec(s.name().clone(), vec![0])),
1205 _ => self.as_materialized_series().arg_unique(),
1206 }
1207 }
1208
1209 pub fn bit_repr(&self) -> Option<BitRepr> {
1210 self.as_materialized_series().bit_repr()
1212 }
1213
1214 pub fn into_frame(self) -> DataFrame {
1215 unsafe { DataFrame::new_no_checks(self.len(), vec![self]) }
1217 }
1218
1219 pub fn extend(&mut self, other: &Column) -> PolarsResult<&mut Self> {
1220 self.into_materialized_series()
1222 .extend(other.as_materialized_series())?;
1223 Ok(self)
1224 }
1225
1226 pub fn rechunk(&self) -> Column {
1227 match self {
1228 Column::Series(s) => s.rechunk().into(),
1229 Column::Partitioned(s) => {
1230 if let Some(s) = s.lazy_as_materialized_series() {
1231 debug_assert_eq!(s.n_chunks(), 1)
1233 }
1234 self.clone()
1235 },
1236 Column::Scalar(s) => {
1237 if s.lazy_as_materialized_series()
1238 .filter(|x| x.n_chunks() > 1)
1239 .is_some()
1240 {
1241 Column::Scalar(ScalarColumn::new(
1242 s.name().clone(),
1243 s.scalar().clone(),
1244 s.len(),
1245 ))
1246 } else {
1247 self.clone()
1248 }
1249 },
1250 }
1251 }
1252
1253 pub fn explode(&self, skip_empty: bool) -> PolarsResult<Column> {
1254 self.as_materialized_series()
1255 .explode(skip_empty)
1256 .map(Column::from)
1257 }
1258 pub fn implode(&self) -> PolarsResult<ListChunked> {
1259 self.as_materialized_series().implode()
1260 }
1261
1262 pub fn fill_null(&self, strategy: FillNullStrategy) -> PolarsResult<Self> {
1263 self.as_materialized_series()
1265 .fill_null(strategy)
1266 .map(Column::from)
1267 }
1268
1269 pub fn divide(&self, rhs: &Column) -> PolarsResult<Self> {
1270 self.as_materialized_series()
1272 .divide(rhs.as_materialized_series())
1273 .map(Column::from)
1274 }
1275
1276 pub fn shift(&self, periods: i64) -> Column {
1277 self.as_materialized_series().shift(periods).into()
1279 }
1280
1281 #[cfg(feature = "zip_with")]
1282 pub fn zip_with(&self, mask: &BooleanChunked, other: &Self) -> PolarsResult<Self> {
1283 self.as_materialized_series()
1285 .zip_with(mask, other.as_materialized_series())
1286 .map(Self::from)
1287 }
1288
1289 #[cfg(feature = "zip_with")]
1290 pub fn zip_with_same_type(
1291 &self,
1292 mask: &ChunkedArray<BooleanType>,
1293 other: &Column,
1294 ) -> PolarsResult<Column> {
1295 self.as_materialized_series()
1297 .zip_with_same_type(mask, other.as_materialized_series())
1298 .map(Column::from)
1299 }
1300
1301 pub fn drop_nulls(&self) -> Column {
1302 match self {
1303 Column::Series(s) => s.drop_nulls().into_column(),
1304 Column::Partitioned(s) => s.as_materialized_series().drop_nulls().into_column(),
1306 Column::Scalar(s) => s.drop_nulls().into_column(),
1307 }
1308 }
1309
1310 pub fn as_list(&self) -> ListChunked {
1312 self.as_materialized_series().as_list()
1315 }
1316
1317 pub fn is_sorted_flag(&self) -> IsSorted {
1318 match self {
1319 Column::Series(s) => s.is_sorted_flag(),
1320 Column::Partitioned(s) => s.partitions().is_sorted_flag(),
1321 Column::Scalar(_) => IsSorted::Ascending,
1322 }
1323 }
1324
1325 pub fn unique(&self) -> PolarsResult<Column> {
1326 match self {
1327 Column::Series(s) => s.unique().map(Column::from),
1328 Column::Partitioned(s) => s.as_materialized_series().unique().map(Column::from),
1330 Column::Scalar(s) => {
1331 _ = s.as_single_value_series().unique()?;
1332 if s.is_empty() {
1333 return Ok(s.clone().into_column());
1334 }
1335
1336 Ok(s.resize(1).into_column())
1337 },
1338 }
1339 }
1340 pub fn unique_stable(&self) -> PolarsResult<Column> {
1341 match self {
1342 Column::Series(s) => s.unique_stable().map(Column::from),
1343 Column::Partitioned(s) => s.as_materialized_series().unique_stable().map(Column::from),
1345 Column::Scalar(s) => {
1346 _ = s.as_single_value_series().unique_stable()?;
1347 if s.is_empty() {
1348 return Ok(s.clone().into_column());
1349 }
1350
1351 Ok(s.resize(1).into_column())
1352 },
1353 }
1354 }
1355
1356 pub fn reshape_list(&self, dimensions: &[ReshapeDimension]) -> PolarsResult<Self> {
1357 self.as_materialized_series()
1359 .reshape_list(dimensions)
1360 .map(Self::from)
1361 }
1362
1363 #[cfg(feature = "dtype-array")]
1364 pub fn reshape_array(&self, dimensions: &[ReshapeDimension]) -> PolarsResult<Self> {
1365 self.as_materialized_series()
1367 .reshape_array(dimensions)
1368 .map(Self::from)
1369 }
1370
1371 pub fn sort(&self, sort_options: SortOptions) -> PolarsResult<Self> {
1372 self.as_materialized_series()
1374 .sort(sort_options)
1375 .map(Self::from)
1376 }
1377
1378 pub fn filter(&self, filter: &BooleanChunked) -> PolarsResult<Self> {
1379 match self {
1380 Column::Series(s) => s.filter(filter).map(Column::from),
1381 Column::Partitioned(s) => s.as_materialized_series().filter(filter).map(Column::from),
1382 Column::Scalar(s) => {
1383 if s.is_empty() {
1384 return Ok(s.clone().into_column());
1385 }
1386
1387 if filter.len() == 1 {
1389 return match filter.get(0) {
1390 Some(true) => Ok(s.clone().into_column()),
1391 _ => Ok(s.resize(0).into_column()),
1392 };
1393 }
1394
1395 Ok(s.resize(filter.sum().unwrap() as usize).into_column())
1396 },
1397 }
1398 }
1399
1400 #[cfg(feature = "random")]
1401 pub fn shuffle(&self, seed: Option<u64>) -> Self {
1402 self.as_materialized_series().shuffle(seed).into()
1404 }
1405
1406 #[cfg(feature = "random")]
1407 pub fn sample_frac(
1408 &self,
1409 frac: f64,
1410 with_replacement: bool,
1411 shuffle: bool,
1412 seed: Option<u64>,
1413 ) -> PolarsResult<Self> {
1414 self.as_materialized_series()
1415 .sample_frac(frac, with_replacement, shuffle, seed)
1416 .map(Self::from)
1417 }
1418
1419 #[cfg(feature = "random")]
1420 pub fn sample_n(
1421 &self,
1422 n: usize,
1423 with_replacement: bool,
1424 shuffle: bool,
1425 seed: Option<u64>,
1426 ) -> PolarsResult<Self> {
1427 self.as_materialized_series()
1428 .sample_n(n, with_replacement, shuffle, seed)
1429 .map(Self::from)
1430 }
1431
1432 pub fn gather_every(&self, n: usize, offset: usize) -> PolarsResult<Column> {
1433 polars_ensure!(n > 0, InvalidOperation: "gather_every(n): n should be positive");
1434 if self.len().saturating_sub(offset) == 0 {
1435 return Ok(self.clear());
1436 }
1437
1438 match self {
1439 Column::Series(s) => Ok(s.gather_every(n, offset)?.into()),
1440 Column::Partitioned(s) => {
1441 Ok(s.as_materialized_series().gather_every(n, offset)?.into())
1442 },
1443 Column::Scalar(s) => {
1444 let total = s.len() - offset;
1445 Ok(s.resize(1 + (total - 1) / n).into())
1446 },
1447 }
1448 }
1449
1450 pub fn extend_constant(&self, value: AnyValue, n: usize) -> PolarsResult<Self> {
1451 if self.is_empty() {
1452 return Ok(Self::new_scalar(
1453 self.name().clone(),
1454 Scalar::new(self.dtype().clone(), value.into_static()),
1455 n,
1456 ));
1457 }
1458
1459 match self {
1460 Column::Series(s) => s.extend_constant(value, n).map(Column::from),
1461 Column::Partitioned(s) => s.extend_constant(value, n).map(Column::from),
1462 Column::Scalar(s) => {
1463 if s.scalar().as_any_value() == value {
1464 Ok(s.resize(s.len() + n).into())
1465 } else {
1466 s.as_materialized_series()
1467 .extend_constant(value, n)
1468 .map(Column::from)
1469 }
1470 },
1471 }
1472 }
1473
1474 pub fn is_finite(&self) -> PolarsResult<BooleanChunked> {
1475 self.try_map_unary_elementwise_to_bool(|s| s.is_finite())
1476 }
1477 pub fn is_infinite(&self) -> PolarsResult<BooleanChunked> {
1478 self.try_map_unary_elementwise_to_bool(|s| s.is_infinite())
1479 }
1480 pub fn is_nan(&self) -> PolarsResult<BooleanChunked> {
1481 self.try_map_unary_elementwise_to_bool(|s| s.is_nan())
1482 }
1483 pub fn is_not_nan(&self) -> PolarsResult<BooleanChunked> {
1484 self.try_map_unary_elementwise_to_bool(|s| s.is_not_nan())
1485 }
1486
1487 pub fn wrapping_trunc_div_scalar<T>(&self, rhs: T) -> Self
1488 where
1489 T: Num + NumCast,
1490 {
1491 self.as_materialized_series()
1493 .wrapping_trunc_div_scalar(rhs)
1494 .into()
1495 }
1496
1497 pub fn product(&self) -> PolarsResult<Scalar> {
1498 self.as_materialized_series().product()
1500 }
1501
1502 pub fn phys_iter(&self) -> SeriesPhysIter<'_> {
1503 self.as_materialized_series().phys_iter()
1505 }
1506
1507 #[inline]
1508 pub fn get(&self, index: usize) -> PolarsResult<AnyValue<'_>> {
1509 polars_ensure!(index < self.len(), oob = index, self.len());
1510
1511 Ok(unsafe { self.get_unchecked(index) })
1513 }
1514 #[inline(always)]
1518 pub unsafe fn get_unchecked(&self, index: usize) -> AnyValue<'_> {
1519 debug_assert!(index < self.len());
1520
1521 match self {
1522 Column::Series(s) => unsafe { s.get_unchecked(index) },
1523 Column::Partitioned(s) => unsafe { s.get_unchecked(index) },
1524 Column::Scalar(s) => s.scalar().as_any_value(),
1525 }
1526 }
1527
1528 #[cfg(feature = "object")]
1529 pub fn get_object(
1530 &self,
1531 index: usize,
1532 ) -> Option<&dyn crate::chunked_array::object::PolarsObjectSafe> {
1533 self.as_materialized_series().get_object(index)
1534 }
1535
1536 pub fn bitand(&self, rhs: &Self) -> PolarsResult<Self> {
1537 self.try_apply_broadcasting_binary_elementwise(rhs, |l, r| l & r)
1538 }
1539 pub fn bitor(&self, rhs: &Self) -> PolarsResult<Self> {
1540 self.try_apply_broadcasting_binary_elementwise(rhs, |l, r| l | r)
1541 }
1542 pub fn bitxor(&self, rhs: &Self) -> PolarsResult<Self> {
1543 self.try_apply_broadcasting_binary_elementwise(rhs, |l, r| l ^ r)
1544 }
1545
1546 pub fn try_add_owned(self, other: Self) -> PolarsResult<Self> {
1547 match (self, other) {
1548 (Column::Series(lhs), Column::Series(rhs)) => {
1549 lhs.take().try_add_owned(rhs.take()).map(Column::from)
1550 },
1551 (lhs, rhs) => lhs + rhs,
1552 }
1553 }
1554 pub fn try_sub_owned(self, other: Self) -> PolarsResult<Self> {
1555 match (self, other) {
1556 (Column::Series(lhs), Column::Series(rhs)) => {
1557 lhs.take().try_sub_owned(rhs.take()).map(Column::from)
1558 },
1559 (lhs, rhs) => lhs - rhs,
1560 }
1561 }
1562 pub fn try_mul_owned(self, other: Self) -> PolarsResult<Self> {
1563 match (self, other) {
1564 (Column::Series(lhs), Column::Series(rhs)) => {
1565 lhs.take().try_mul_owned(rhs.take()).map(Column::from)
1566 },
1567 (lhs, rhs) => lhs * rhs,
1568 }
1569 }
1570
1571 pub(crate) fn str_value(&self, index: usize) -> PolarsResult<Cow<'_, str>> {
1572 Ok(self.get(index)?.str_value())
1573 }
1574
1575 pub fn min_reduce(&self) -> PolarsResult<Scalar> {
1576 match self {
1577 Column::Series(s) => s.min_reduce(),
1578 Column::Partitioned(s) => s.min_reduce(),
1579 Column::Scalar(s) => {
1580 s.as_single_value_series().min_reduce()
1583 },
1584 }
1585 }
1586 pub fn max_reduce(&self) -> PolarsResult<Scalar> {
1587 match self {
1588 Column::Series(s) => s.max_reduce(),
1589 Column::Partitioned(s) => s.max_reduce(),
1590 Column::Scalar(s) => {
1591 s.as_single_value_series().max_reduce()
1594 },
1595 }
1596 }
1597 pub fn median_reduce(&self) -> PolarsResult<Scalar> {
1598 match self {
1599 Column::Series(s) => s.median_reduce(),
1600 Column::Partitioned(s) => s.as_materialized_series().median_reduce(),
1601 Column::Scalar(s) => {
1602 s.as_single_value_series().median_reduce()
1605 },
1606 }
1607 }
1608 pub fn mean_reduce(&self) -> PolarsResult<Scalar> {
1609 match self {
1610 Column::Series(s) => s.mean_reduce(),
1611 Column::Partitioned(s) => s.as_materialized_series().mean_reduce(),
1612 Column::Scalar(s) => {
1613 s.as_single_value_series().mean_reduce()
1616 },
1617 }
1618 }
1619 pub fn std_reduce(&self, ddof: u8) -> PolarsResult<Scalar> {
1620 match self {
1621 Column::Series(s) => s.std_reduce(ddof),
1622 Column::Partitioned(s) => s.as_materialized_series().std_reduce(ddof),
1623 Column::Scalar(s) => {
1624 let n = s.len().min(ddof as usize + 1);
1627 s.as_n_values_series(n).std_reduce(ddof)
1628 },
1629 }
1630 }
1631 pub fn var_reduce(&self, ddof: u8) -> PolarsResult<Scalar> {
1632 match self {
1633 Column::Series(s) => s.var_reduce(ddof),
1634 Column::Partitioned(s) => s.as_materialized_series().var_reduce(ddof),
1635 Column::Scalar(s) => {
1636 let n = s.len().min(ddof as usize + 1);
1639 s.as_n_values_series(n).var_reduce(ddof)
1640 },
1641 }
1642 }
1643 pub fn sum_reduce(&self) -> PolarsResult<Scalar> {
1644 self.as_materialized_series().sum_reduce()
1647 }
1648 pub fn and_reduce(&self) -> PolarsResult<Scalar> {
1649 match self {
1650 Column::Series(s) => s.and_reduce(),
1651 Column::Partitioned(s) => s.and_reduce(),
1652 Column::Scalar(s) => {
1653 s.as_single_value_series().and_reduce()
1656 },
1657 }
1658 }
1659 pub fn or_reduce(&self) -> PolarsResult<Scalar> {
1660 match self {
1661 Column::Series(s) => s.or_reduce(),
1662 Column::Partitioned(s) => s.or_reduce(),
1663 Column::Scalar(s) => {
1664 s.as_single_value_series().or_reduce()
1667 },
1668 }
1669 }
1670 pub fn xor_reduce(&self) -> PolarsResult<Scalar> {
1671 match self {
1672 Column::Series(s) => s.xor_reduce(),
1673 Column::Partitioned(s) => s.as_materialized_series().xor_reduce(),
1675 Column::Scalar(s) => {
1676 s.as_n_values_series(2 - s.len() % 2).xor_reduce()
1683 },
1684 }
1685 }
1686 pub fn n_unique(&self) -> PolarsResult<usize> {
1687 match self {
1688 Column::Series(s) => s.n_unique(),
1689 Column::Partitioned(s) => s.partitions().n_unique(),
1690 Column::Scalar(s) => s.as_single_value_series().n_unique(),
1691 }
1692 }
1693 pub fn quantile_reduce(&self, quantile: f64, method: QuantileMethod) -> PolarsResult<Scalar> {
1694 self.as_materialized_series()
1695 .quantile_reduce(quantile, method)
1696 }
1697
1698 pub(crate) fn estimated_size(&self) -> usize {
1699 self.as_materialized_series().estimated_size()
1701 }
1702
1703 pub fn sort_with(&self, options: SortOptions) -> PolarsResult<Self> {
1704 match self {
1705 Column::Series(s) => s.sort_with(options).map(Self::from),
1706 Column::Partitioned(s) => s
1708 .as_materialized_series()
1709 .sort_with(options)
1710 .map(Self::from),
1711 Column::Scalar(s) => {
1712 _ = s.as_single_value_series().sort_with(options)?;
1714
1715 Ok(self.clone())
1716 },
1717 }
1718 }
1719
1720 pub fn map_unary_elementwise_to_bool(
1721 &self,
1722 f: impl Fn(&Series) -> BooleanChunked,
1723 ) -> BooleanChunked {
1724 self.try_map_unary_elementwise_to_bool(|s| Ok(f(s)))
1725 .unwrap()
1726 }
1727 pub fn try_map_unary_elementwise_to_bool(
1728 &self,
1729 f: impl Fn(&Series) -> PolarsResult<BooleanChunked>,
1730 ) -> PolarsResult<BooleanChunked> {
1731 match self {
1732 Column::Series(s) => f(s),
1733 Column::Partitioned(s) => f(s.as_materialized_series()),
1734 Column::Scalar(s) => Ok(f(&s.as_single_value_series())?.new_from_index(0, s.len())),
1735 }
1736 }
1737
1738 pub fn apply_unary_elementwise(&self, f: impl Fn(&Series) -> Series) -> Column {
1739 self.try_apply_unary_elementwise(|s| Ok(f(s))).unwrap()
1740 }
1741 pub fn try_apply_unary_elementwise(
1742 &self,
1743 f: impl Fn(&Series) -> PolarsResult<Series>,
1744 ) -> PolarsResult<Column> {
1745 match self {
1746 Column::Series(s) => f(s).map(Column::from),
1747 Column::Partitioned(s) => s.try_apply_unary_elementwise(f).map(Self::from),
1748 Column::Scalar(s) => Ok(ScalarColumn::from_single_value_series(
1749 f(&s.as_single_value_series())?,
1750 s.len(),
1751 )
1752 .into()),
1753 }
1754 }
1755
1756 pub fn apply_broadcasting_binary_elementwise(
1757 &self,
1758 other: &Self,
1759 op: impl Fn(&Series, &Series) -> Series,
1760 ) -> PolarsResult<Column> {
1761 self.try_apply_broadcasting_binary_elementwise(other, |lhs, rhs| Ok(op(lhs, rhs)))
1762 }
1763 pub fn try_apply_broadcasting_binary_elementwise(
1764 &self,
1765 other: &Self,
1766 op: impl Fn(&Series, &Series) -> PolarsResult<Series>,
1767 ) -> PolarsResult<Column> {
1768 fn output_length(a: &Column, b: &Column) -> PolarsResult<usize> {
1769 match (a.len(), b.len()) {
1770 (1, o) | (o, 1) => Ok(o),
1772 (a, b) if a == b => Ok(a),
1774 (a, b) => {
1776 polars_bail!(InvalidOperation: "cannot do a binary operation on columns of different lengths: got {} and {}", a, b)
1777 },
1778 }
1779 }
1780
1781 let length = output_length(self, other)?;
1783 match (self, other) {
1784 (Column::Series(lhs), Column::Series(rhs)) => op(lhs, rhs).map(Column::from),
1785 (Column::Series(lhs), Column::Scalar(rhs)) => {
1786 op(lhs, &rhs.as_single_value_series()).map(Column::from)
1787 },
1788 (Column::Scalar(lhs), Column::Series(rhs)) => {
1789 op(&lhs.as_single_value_series(), rhs).map(Column::from)
1790 },
1791 (Column::Scalar(lhs), Column::Scalar(rhs)) => {
1792 let lhs = lhs.as_single_value_series();
1793 let rhs = rhs.as_single_value_series();
1794
1795 Ok(ScalarColumn::from_single_value_series(op(&lhs, &rhs)?, length).into_column())
1796 },
1797 (lhs, rhs) => {
1799 op(lhs.as_materialized_series(), rhs.as_materialized_series()).map(Column::from)
1800 },
1801 }
1802 }
1803
1804 pub fn apply_binary_elementwise(
1805 &self,
1806 other: &Self,
1807 f: impl Fn(&Series, &Series) -> Series,
1808 f_lb: impl Fn(&Scalar, &Series) -> Series,
1809 f_rb: impl Fn(&Series, &Scalar) -> Series,
1810 ) -> Column {
1811 self.try_apply_binary_elementwise(
1812 other,
1813 |lhs, rhs| Ok(f(lhs, rhs)),
1814 |lhs, rhs| Ok(f_lb(lhs, rhs)),
1815 |lhs, rhs| Ok(f_rb(lhs, rhs)),
1816 )
1817 .unwrap()
1818 }
1819 pub fn try_apply_binary_elementwise(
1820 &self,
1821 other: &Self,
1822 f: impl Fn(&Series, &Series) -> PolarsResult<Series>,
1823 f_lb: impl Fn(&Scalar, &Series) -> PolarsResult<Series>,
1824 f_rb: impl Fn(&Series, &Scalar) -> PolarsResult<Series>,
1825 ) -> PolarsResult<Column> {
1826 debug_assert_eq!(self.len(), other.len());
1827
1828 match (self, other) {
1829 (Column::Series(lhs), Column::Series(rhs)) => f(lhs, rhs).map(Column::from),
1830 (Column::Series(lhs), Column::Scalar(rhs)) => f_rb(lhs, rhs.scalar()).map(Column::from),
1831 (Column::Scalar(lhs), Column::Series(rhs)) => f_lb(lhs.scalar(), rhs).map(Column::from),
1832 (Column::Scalar(lhs), Column::Scalar(rhs)) => {
1833 let lhs = lhs.as_single_value_series();
1834 let rhs = rhs.as_single_value_series();
1835
1836 Ok(
1837 ScalarColumn::from_single_value_series(f(&lhs, &rhs)?, self.len())
1838 .into_column(),
1839 )
1840 },
1841 (lhs, rhs) => {
1843 f(lhs.as_materialized_series(), rhs.as_materialized_series()).map(Column::from)
1844 },
1845 }
1846 }
1847
1848 #[cfg(feature = "approx_unique")]
1849 pub fn approx_n_unique(&self) -> PolarsResult<IdxSize> {
1850 match self {
1851 Column::Series(s) => s.approx_n_unique(),
1852 Column::Partitioned(s) => s.as_materialized_series().approx_n_unique(),
1854 Column::Scalar(s) => {
1855 s.as_single_value_series().approx_n_unique()?;
1857 Ok(1)
1858 },
1859 }
1860 }
1861
1862 pub fn n_chunks(&self) -> usize {
1863 match self {
1864 Column::Series(s) => s.n_chunks(),
1865 Column::Scalar(s) => s.lazy_as_materialized_series().map_or(1, |x| x.n_chunks()),
1866 Column::Partitioned(s) => {
1867 if let Some(s) = s.lazy_as_materialized_series() {
1868 debug_assert_eq!(s.n_chunks(), 1)
1870 }
1871 1
1872 },
1873 }
1874 }
1875
1876 #[expect(clippy::wrong_self_convention)]
1877 pub(crate) fn into_total_ord_inner<'a>(&'a self) -> Box<dyn TotalOrdInner + 'a> {
1878 self.as_materialized_series().into_total_ord_inner()
1880 }
1881 #[expect(unused, clippy::wrong_self_convention)]
1882 pub(crate) fn into_total_eq_inner<'a>(&'a self) -> Box<dyn TotalEqInner + 'a> {
1883 self.as_materialized_series().into_total_eq_inner()
1885 }
1886
1887 pub fn rechunk_to_arrow(self, compat_level: CompatLevel) -> Box<dyn Array> {
1888 let mut series = self.take_materialized_series();
1890 if series.n_chunks() > 1 {
1891 series = series.rechunk();
1892 }
1893 series.to_arrow(0, compat_level)
1894 }
1895
1896 pub fn trim_lists_to_normalized_offsets(&self) -> Option<Column> {
1897 self.as_materialized_series()
1898 .trim_lists_to_normalized_offsets()
1899 .map(Column::from)
1900 }
1901
1902 pub fn propagate_nulls(&self) -> Option<Column> {
1903 self.as_materialized_series()
1904 .propagate_nulls()
1905 .map(Column::from)
1906 }
1907
1908 pub fn deposit(&self, validity: &Bitmap) -> Column {
1909 self.as_materialized_series()
1910 .deposit(validity)
1911 .into_column()
1912 }
1913
1914 pub fn rechunk_validity(&self) -> Option<Bitmap> {
1915 self.as_materialized_series().rechunk_validity()
1917 }
1918}
1919
1920impl Default for Column {
1921 fn default() -> Self {
1922 Self::new_scalar(
1923 PlSmallStr::EMPTY,
1924 Scalar::new(DataType::Int64, AnyValue::Null),
1925 0,
1926 )
1927 }
1928}
1929
1930impl PartialEq for Column {
1931 fn eq(&self, other: &Self) -> bool {
1932 self.as_materialized_series()
1934 .eq(other.as_materialized_series())
1935 }
1936}
1937
1938impl From<Series> for Column {
1939 #[inline]
1940 fn from(series: Series) -> Self {
1941 if series.len() == 1 {
1944 return Self::Scalar(ScalarColumn::unit_scalar_from_series(series));
1945 }
1946
1947 Self::Series(SeriesColumn::new(series))
1948 }
1949}
1950
1951impl<T: IntoSeries> IntoColumn for T {
1952 #[inline]
1953 fn into_column(self) -> Column {
1954 self.into_series().into()
1955 }
1956}
1957
1958impl IntoColumn for Column {
1959 #[inline(always)]
1960 fn into_column(self) -> Column {
1961 self
1962 }
1963}
1964
1965#[derive(Clone)]
1970#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1971#[cfg_attr(feature = "serde", serde(into = "Series"))]
1972struct _SerdeSeries(Series);
1973
1974impl From<Column> for _SerdeSeries {
1975 #[inline]
1976 fn from(value: Column) -> Self {
1977 Self(value.take_materialized_series())
1978 }
1979}
1980
1981impl From<_SerdeSeries> for Series {
1982 #[inline]
1983 fn from(value: _SerdeSeries) -> Self {
1984 value.0
1985 }
1986}