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))]
42pub enum Column {
43 Series(SeriesColumn),
44 Partitioned(PartitionedColumn),
45 Scalar(ScalarColumn),
46}
47
48pub trait IntoColumn: Sized {
50 fn into_column(self) -> Column;
51}
52
53impl Column {
54 #[inline]
55 #[track_caller]
56 pub fn new<T, Phantom>(name: PlSmallStr, values: T) -> Self
57 where
58 Phantom: ?Sized,
59 Series: NamedFrom<T, Phantom>,
60 {
61 Self::Series(SeriesColumn::new(NamedFrom::new(name, values)))
62 }
63
64 #[inline]
65 pub fn new_empty(name: PlSmallStr, dtype: &DataType) -> Self {
66 Self::new_scalar(name, Scalar::new(dtype.clone(), AnyValue::Null), 0)
67 }
68
69 #[inline]
70 pub fn new_scalar(name: PlSmallStr, scalar: Scalar, length: usize) -> Self {
71 Self::Scalar(ScalarColumn::new(name, scalar, length))
72 }
73
74 #[inline]
75 pub fn new_partitioned(name: PlSmallStr, scalar: Scalar, length: usize) -> Self {
76 Self::Scalar(ScalarColumn::new(name, scalar, length))
77 }
78
79 pub fn new_row_index(name: PlSmallStr, offset: IdxSize, length: usize) -> PolarsResult<Column> {
80 let length = IdxSize::try_from(length).unwrap_or(IdxSize::MAX);
81
82 if offset.checked_add(length).is_none() {
83 polars_bail!(
84 ComputeError:
85 "row index with offset {} overflows on dataframe with height {}",
86 offset, length
87 )
88 }
89
90 let range = offset..offset + length;
91
92 let mut ca = IdxCa::from_vec(name, range.collect());
93 ca.set_sorted_flag(IsSorted::Ascending);
94 let col = ca.into_series().into();
95
96 Ok(col)
97 }
98
99 #[inline]
104 pub fn as_materialized_series(&self) -> &Series {
105 match self {
106 Column::Series(s) => s,
107 Column::Partitioned(s) => s.as_materialized_series(),
108 Column::Scalar(s) => s.as_materialized_series(),
109 }
110 }
111 #[inline]
115 pub fn into_materialized_series(&mut self) -> &mut Series {
116 match self {
117 Column::Series(s) => s,
118 Column::Partitioned(s) => {
119 let series = std::mem::replace(
120 s,
121 PartitionedColumn::new_empty(PlSmallStr::EMPTY, DataType::Null),
122 )
123 .take_materialized_series();
124 *self = Column::Series(series.into());
125 let Column::Series(s) = self else {
126 unreachable!();
127 };
128 s
129 },
130 Column::Scalar(s) => {
131 let series = std::mem::replace(
132 s,
133 ScalarColumn::new_empty(PlSmallStr::EMPTY, DataType::Null),
134 )
135 .take_materialized_series();
136 *self = Column::Series(series.into());
137 let Column::Series(s) = self else {
138 unreachable!();
139 };
140 s
141 },
142 }
143 }
144 #[inline]
148 pub fn take_materialized_series(self) -> Series {
149 match self {
150 Column::Series(s) => s.take(),
151 Column::Partitioned(s) => s.take_materialized_series(),
152 Column::Scalar(s) => s.take_materialized_series(),
153 }
154 }
155
156 #[inline]
157 pub fn dtype(&self) -> &DataType {
158 match self {
159 Column::Series(s) => s.dtype(),
160 Column::Partitioned(s) => s.dtype(),
161 Column::Scalar(s) => s.dtype(),
162 }
163 }
164
165 #[inline]
166 pub fn field(&self) -> Cow<Field> {
167 match self {
168 Column::Series(s) => s.field(),
169 Column::Partitioned(s) => s.field(),
170 Column::Scalar(s) => match s.lazy_as_materialized_series() {
171 None => Cow::Owned(Field::new(s.name().clone(), s.dtype().clone())),
172 Some(s) => s.field(),
173 },
174 }
175 }
176
177 #[inline]
178 pub fn name(&self) -> &PlSmallStr {
179 match self {
180 Column::Series(s) => s.name(),
181 Column::Partitioned(s) => s.name(),
182 Column::Scalar(s) => s.name(),
183 }
184 }
185
186 #[inline]
187 pub fn len(&self) -> usize {
188 match self {
189 Column::Series(s) => s.len(),
190 Column::Partitioned(s) => s.len(),
191 Column::Scalar(s) => s.len(),
192 }
193 }
194
195 #[inline]
196 pub fn with_name(mut self, name: PlSmallStr) -> Column {
197 self.rename(name);
198 self
199 }
200
201 #[inline]
202 pub fn rename(&mut self, name: PlSmallStr) {
203 match self {
204 Column::Series(s) => _ = s.rename(name),
205 Column::Partitioned(s) => _ = s.rename(name),
206 Column::Scalar(s) => _ = s.rename(name),
207 }
208 }
209
210 #[inline]
212 pub fn as_series(&self) -> Option<&Series> {
213 match self {
214 Column::Series(s) => Some(s),
215 _ => None,
216 }
217 }
218 #[inline]
219 pub fn as_partitioned_column(&self) -> Option<&PartitionedColumn> {
220 match self {
221 Column::Partitioned(s) => Some(s),
222 _ => None,
223 }
224 }
225 #[inline]
226 pub fn as_scalar_column(&self) -> Option<&ScalarColumn> {
227 match self {
228 Column::Scalar(s) => Some(s),
229 _ => None,
230 }
231 }
232 #[inline]
233 pub fn as_scalar_column_mut(&mut self) -> Option<&mut ScalarColumn> {
234 match self {
235 Column::Scalar(s) => Some(s),
236 _ => None,
237 }
238 }
239
240 pub fn try_bool(&self) -> Option<&BooleanChunked> {
242 self.as_materialized_series().try_bool()
243 }
244 pub fn try_i8(&self) -> Option<&Int8Chunked> {
245 self.as_materialized_series().try_i8()
246 }
247 pub fn try_i16(&self) -> Option<&Int16Chunked> {
248 self.as_materialized_series().try_i16()
249 }
250 pub fn try_i32(&self) -> Option<&Int32Chunked> {
251 self.as_materialized_series().try_i32()
252 }
253 pub fn try_i64(&self) -> Option<&Int64Chunked> {
254 self.as_materialized_series().try_i64()
255 }
256 pub fn try_u8(&self) -> Option<&UInt8Chunked> {
257 self.as_materialized_series().try_u8()
258 }
259 pub fn try_u16(&self) -> Option<&UInt16Chunked> {
260 self.as_materialized_series().try_u16()
261 }
262 pub fn try_u32(&self) -> Option<&UInt32Chunked> {
263 self.as_materialized_series().try_u32()
264 }
265 pub fn try_u64(&self) -> Option<&UInt64Chunked> {
266 self.as_materialized_series().try_u64()
267 }
268 pub fn try_f32(&self) -> Option<&Float32Chunked> {
269 self.as_materialized_series().try_f32()
270 }
271 pub fn try_f64(&self) -> Option<&Float64Chunked> {
272 self.as_materialized_series().try_f64()
273 }
274 pub fn try_str(&self) -> Option<&StringChunked> {
275 self.as_materialized_series().try_str()
276 }
277 pub fn try_list(&self) -> Option<&ListChunked> {
278 self.as_materialized_series().try_list()
279 }
280 pub fn try_binary(&self) -> Option<&BinaryChunked> {
281 self.as_materialized_series().try_binary()
282 }
283 pub fn try_idx(&self) -> Option<&IdxCa> {
284 self.as_materialized_series().try_idx()
285 }
286 pub fn try_binary_offset(&self) -> Option<&BinaryOffsetChunked> {
287 self.as_materialized_series().try_binary_offset()
288 }
289 #[cfg(feature = "dtype-datetime")]
290 pub fn try_datetime(&self) -> Option<&DatetimeChunked> {
291 self.as_materialized_series().try_datetime()
292 }
293 #[cfg(feature = "dtype-struct")]
294 pub fn try_struct(&self) -> Option<&StructChunked> {
295 self.as_materialized_series().try_struct()
296 }
297 #[cfg(feature = "dtype-decimal")]
298 pub fn try_decimal(&self) -> Option<&DecimalChunked> {
299 self.as_materialized_series().try_decimal()
300 }
301 #[cfg(feature = "dtype-array")]
302 pub fn try_array(&self) -> Option<&ArrayChunked> {
303 self.as_materialized_series().try_array()
304 }
305 #[cfg(feature = "dtype-categorical")]
306 pub fn try_categorical(&self) -> Option<&CategoricalChunked> {
307 self.as_materialized_series().try_categorical()
308 }
309 #[cfg(feature = "dtype-date")]
310 pub fn try_date(&self) -> Option<&DateChunked> {
311 self.as_materialized_series().try_date()
312 }
313 #[cfg(feature = "dtype-duration")]
314 pub fn try_duration(&self) -> Option<&DurationChunked> {
315 self.as_materialized_series().try_duration()
316 }
317
318 pub fn bool(&self) -> PolarsResult<&BooleanChunked> {
320 self.as_materialized_series().bool()
321 }
322 pub fn i8(&self) -> PolarsResult<&Int8Chunked> {
323 self.as_materialized_series().i8()
324 }
325 pub fn i16(&self) -> PolarsResult<&Int16Chunked> {
326 self.as_materialized_series().i16()
327 }
328 pub fn i32(&self) -> PolarsResult<&Int32Chunked> {
329 self.as_materialized_series().i32()
330 }
331 pub fn i64(&self) -> PolarsResult<&Int64Chunked> {
332 self.as_materialized_series().i64()
333 }
334 #[cfg(feature = "dtype-i128")]
335 pub fn i128(&self) -> PolarsResult<&Int128Chunked> {
336 self.as_materialized_series().i128()
337 }
338 pub fn u8(&self) -> PolarsResult<&UInt8Chunked> {
339 self.as_materialized_series().u8()
340 }
341 pub fn u16(&self) -> PolarsResult<&UInt16Chunked> {
342 self.as_materialized_series().u16()
343 }
344 pub fn u32(&self) -> PolarsResult<&UInt32Chunked> {
345 self.as_materialized_series().u32()
346 }
347 pub fn u64(&self) -> PolarsResult<&UInt64Chunked> {
348 self.as_materialized_series().u64()
349 }
350 pub fn f32(&self) -> PolarsResult<&Float32Chunked> {
351 self.as_materialized_series().f32()
352 }
353 pub fn f64(&self) -> PolarsResult<&Float64Chunked> {
354 self.as_materialized_series().f64()
355 }
356 pub fn str(&self) -> PolarsResult<&StringChunked> {
357 self.as_materialized_series().str()
358 }
359 pub fn list(&self) -> PolarsResult<&ListChunked> {
360 self.as_materialized_series().list()
361 }
362 pub fn binary(&self) -> PolarsResult<&BinaryChunked> {
363 self.as_materialized_series().binary()
364 }
365 pub fn idx(&self) -> PolarsResult<&IdxCa> {
366 self.as_materialized_series().idx()
367 }
368 pub fn binary_offset(&self) -> PolarsResult<&BinaryOffsetChunked> {
369 self.as_materialized_series().binary_offset()
370 }
371 #[cfg(feature = "dtype-datetime")]
372 pub fn datetime(&self) -> PolarsResult<&DatetimeChunked> {
373 self.as_materialized_series().datetime()
374 }
375 #[cfg(feature = "dtype-struct")]
376 pub fn struct_(&self) -> PolarsResult<&StructChunked> {
377 self.as_materialized_series().struct_()
378 }
379 #[cfg(feature = "dtype-decimal")]
380 pub fn decimal(&self) -> PolarsResult<&DecimalChunked> {
381 self.as_materialized_series().decimal()
382 }
383 #[cfg(feature = "dtype-array")]
384 pub fn array(&self) -> PolarsResult<&ArrayChunked> {
385 self.as_materialized_series().array()
386 }
387 #[cfg(feature = "dtype-categorical")]
388 pub fn categorical(&self) -> PolarsResult<&CategoricalChunked> {
389 self.as_materialized_series().categorical()
390 }
391 #[cfg(feature = "dtype-date")]
392 pub fn date(&self) -> PolarsResult<&DateChunked> {
393 self.as_materialized_series().date()
394 }
395 #[cfg(feature = "dtype-duration")]
396 pub fn duration(&self) -> PolarsResult<&DurationChunked> {
397 self.as_materialized_series().duration()
398 }
399
400 pub fn cast_with_options(&self, dtype: &DataType, options: CastOptions) -> PolarsResult<Self> {
402 match self {
403 Column::Series(s) => s.cast_with_options(dtype, options).map(Column::from),
404 Column::Partitioned(s) => s.cast_with_options(dtype, options).map(Column::from),
405 Column::Scalar(s) => s.cast_with_options(dtype, options).map(Column::from),
406 }
407 }
408 pub fn strict_cast(&self, dtype: &DataType) -> PolarsResult<Self> {
409 match self {
410 Column::Series(s) => s.strict_cast(dtype).map(Column::from),
411 Column::Partitioned(s) => s.strict_cast(dtype).map(Column::from),
412 Column::Scalar(s) => s.strict_cast(dtype).map(Column::from),
413 }
414 }
415 pub fn cast(&self, dtype: &DataType) -> PolarsResult<Column> {
416 match self {
417 Column::Series(s) => s.cast(dtype).map(Column::from),
418 Column::Partitioned(s) => s.cast(dtype).map(Column::from),
419 Column::Scalar(s) => s.cast(dtype).map(Column::from),
420 }
421 }
422 pub unsafe fn cast_unchecked(&self, dtype: &DataType) -> PolarsResult<Column> {
426 match self {
427 Column::Series(s) => unsafe { s.cast_unchecked(dtype) }.map(Column::from),
428 Column::Partitioned(s) => unsafe { s.cast_unchecked(dtype) }.map(Column::from),
429 Column::Scalar(s) => unsafe { s.cast_unchecked(dtype) }.map(Column::from),
430 }
431 }
432
433 pub fn clear(&self) -> Self {
434 match self {
435 Column::Series(s) => s.clear().into(),
436 Column::Partitioned(s) => s.clear().into(),
437 Column::Scalar(s) => s.resize(0).into(),
438 }
439 }
440
441 #[inline]
442 pub fn shrink_to_fit(&mut self) {
443 match self {
444 Column::Series(s) => s.shrink_to_fit(),
445 Column::Partitioned(_) => {},
447 Column::Scalar(_) => {},
448 }
449 }
450
451 #[inline]
452 pub fn new_from_index(&self, index: usize, length: usize) -> Self {
453 if index >= self.len() {
454 return Self::full_null(self.name().clone(), length, self.dtype());
455 }
456
457 match self {
458 Column::Series(s) => {
459 let av = unsafe { s.get_unchecked(index) };
461 let scalar = Scalar::new(self.dtype().clone(), av.into_static());
462 Self::new_scalar(self.name().clone(), scalar, length)
463 },
464 Column::Partitioned(s) => {
465 let av = unsafe { s.get_unchecked(index) };
467 let scalar = Scalar::new(self.dtype().clone(), av.into_static());
468 Self::new_scalar(self.name().clone(), scalar, length)
469 },
470 Column::Scalar(s) => s.resize(length).into(),
471 }
472 }
473
474 #[inline]
475 pub fn has_nulls(&self) -> bool {
476 match self {
477 Self::Series(s) => s.has_nulls(),
478 Self::Partitioned(s) => s.as_materialized_series().has_nulls(),
480 Self::Scalar(s) => s.has_nulls(),
481 }
482 }
483
484 #[inline]
485 pub fn is_null(&self) -> BooleanChunked {
486 match self {
487 Self::Series(s) => s.is_null(),
488 Self::Partitioned(s) => s.as_materialized_series().is_null(),
490 Self::Scalar(s) => {
491 BooleanChunked::full(s.name().clone(), s.scalar().is_null(), s.len())
492 },
493 }
494 }
495 #[inline]
496 pub fn is_not_null(&self) -> BooleanChunked {
497 match self {
498 Self::Series(s) => s.is_not_null(),
499 Self::Partitioned(s) => s.as_materialized_series().is_not_null(),
501 Self::Scalar(s) => {
502 BooleanChunked::full(s.name().clone(), !s.scalar().is_null(), s.len())
503 },
504 }
505 }
506
507 pub fn to_physical_repr(&self) -> Column {
508 self.as_materialized_series()
510 .to_physical_repr()
511 .into_owned()
512 .into()
513 }
514 pub unsafe fn from_physical_unchecked(&self, dtype: &DataType) -> PolarsResult<Column> {
518 self.as_materialized_series()
520 .from_physical_unchecked(dtype)
521 .map(Column::from)
522 }
523
524 pub fn head(&self, length: Option<usize>) -> Column {
525 let len = length.unwrap_or(HEAD_DEFAULT_LENGTH);
526 let len = usize::min(len, self.len());
527 self.slice(0, len)
528 }
529 pub fn tail(&self, length: Option<usize>) -> Column {
530 let len = length.unwrap_or(TAIL_DEFAULT_LENGTH);
531 let len = usize::min(len, self.len());
532 debug_assert!(len <= i64::MAX as usize);
533 self.slice(-(len as i64), len)
534 }
535 pub fn slice(&self, offset: i64, length: usize) -> Column {
536 match self {
537 Column::Series(s) => s.slice(offset, length).into(),
538 Column::Partitioned(s) => s.as_materialized_series().slice(offset, length).into(),
540 Column::Scalar(s) => {
541 let (_, length) = slice_offsets(offset, length, s.len());
542 s.resize(length).into()
543 },
544 }
545 }
546
547 pub fn split_at(&self, offset: i64) -> (Column, Column) {
548 let (l, r) = self.as_materialized_series().split_at(offset);
550 (l.into(), r.into())
551 }
552
553 #[inline]
554 pub fn null_count(&self) -> usize {
555 match self {
556 Self::Series(s) => s.null_count(),
557 Self::Partitioned(s) => s.null_count(),
558 Self::Scalar(s) if s.scalar().is_null() => s.len(),
559 Self::Scalar(_) => 0,
560 }
561 }
562
563 pub fn take(&self, indices: &IdxCa) -> PolarsResult<Column> {
564 check_bounds_ca(indices, self.len() as IdxSize)?;
565 Ok(unsafe { self.take_unchecked(indices) })
566 }
567 pub fn take_slice(&self, indices: &[IdxSize]) -> PolarsResult<Column> {
568 check_bounds(indices, self.len() as IdxSize)?;
569 Ok(unsafe { self.take_slice_unchecked(indices) })
570 }
571 pub unsafe fn take_unchecked(&self, indices: &IdxCa) -> Column {
575 debug_assert!(check_bounds_ca(indices, self.len() as IdxSize).is_ok());
576
577 match self {
578 Self::Series(s) => unsafe { s.take_unchecked(indices) }.into(),
579 Self::Partitioned(s) => {
580 let s = s.as_materialized_series();
581 unsafe { s.take_unchecked(indices) }.into()
582 },
583 Self::Scalar(s) => {
584 let idxs_length = indices.len();
585 let idxs_null_count = indices.null_count();
586
587 let scalar = ScalarColumn::from_single_value_series(
588 s.as_single_value_series().take_unchecked(&IdxCa::new(
589 indices.name().clone(),
590 &[0][..s.len().min(1)],
591 )),
592 idxs_length,
593 );
594
595 if idxs_null_count == 0 || scalar.has_nulls() {
597 scalar.into_column()
598 } else if idxs_null_count == idxs_length {
599 scalar.into_nulls().into_column()
600 } else {
601 let validity = indices.rechunk_validity();
602 let series = scalar.take_materialized_series();
603 let name = series.name().clone();
604 let dtype = series.dtype().clone();
605 let mut chunks = series.into_chunks();
606 assert_eq!(chunks.len(), 1);
607 chunks[0] = chunks[0].with_validity(validity);
608 unsafe { Series::from_chunks_and_dtype_unchecked(name, chunks, &dtype) }
609 .into_column()
610 }
611 },
612 }
613 }
614 pub unsafe fn take_slice_unchecked(&self, indices: &[IdxSize]) -> Column {
618 debug_assert!(check_bounds(indices, self.len() as IdxSize).is_ok());
619
620 match self {
621 Self::Series(s) => unsafe { s.take_slice_unchecked(indices) }.into(),
622 Self::Partitioned(s) => {
623 let s = s.as_materialized_series();
624 unsafe { s.take_slice_unchecked(indices) }.into()
625 },
626 Self::Scalar(s) => ScalarColumn::from_single_value_series(
627 s.as_single_value_series()
628 .take_slice_unchecked(&[0][..s.len().min(1)]),
629 indices.len(),
630 )
631 .into(),
632 }
633 }
634
635 #[inline(always)]
637 #[cfg(any(feature = "algorithm_group_by", feature = "bitwise"))]
638 fn agg_with_unit_scalar(
639 &self,
640 groups: &GroupsType,
641 series_agg: impl Fn(&Series, &GroupsType) -> Series,
642 ) -> Column {
643 match self {
644 Column::Series(s) => series_agg(s, groups).into_column(),
645 Column::Partitioned(s) => series_agg(s.as_materialized_series(), groups).into_column(),
647 Column::Scalar(s) => {
648 if s.is_empty() {
649 return series_agg(s.as_materialized_series(), groups).into_column();
650 }
651
652 let series_aggregation = series_agg(
656 &s.as_single_value_series(),
657 &GroupsType::Slice {
658 groups: vec![[0, 1]],
660 rolling: false,
661 },
662 );
663
664 if series_aggregation.has_nulls() {
666 return Self::new_scalar(
667 series_aggregation.name().clone(),
668 Scalar::new(series_aggregation.dtype().clone(), AnyValue::Null),
669 groups.len(),
670 );
671 }
672
673 let mut scalar_col = s.resize(groups.len());
674 if series_aggregation.dtype() != s.dtype() {
677 scalar_col = scalar_col.cast(series_aggregation.dtype()).unwrap();
678 }
679
680 let Some(first_empty_idx) = groups.iter().position(|g| g.is_empty()) else {
681 return scalar_col.into_column();
683 };
684
685 let mut validity = BitmapBuilder::with_capacity(groups.len());
687 validity.extend_constant(first_empty_idx, true);
688 let iter = unsafe {
690 TrustMyLength::new(
691 groups.iter().skip(first_empty_idx).map(|g| !g.is_empty()),
692 groups.len() - first_empty_idx,
693 )
694 };
695 validity.extend_trusted_len_iter(iter);
696
697 let mut s = scalar_col.take_materialized_series().rechunk();
698 let chunks = unsafe { s.chunks_mut() };
700 let arr = &mut chunks[0];
701 *arr = arr.with_validity(validity.into_opt_validity());
702 s.compute_len();
703
704 s.into_column()
705 },
706 }
707 }
708
709 #[cfg(feature = "algorithm_group_by")]
713 pub unsafe fn agg_min(&self, groups: &GroupsType) -> Self {
714 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_min(g) })
715 }
716
717 #[cfg(feature = "algorithm_group_by")]
721 pub unsafe fn agg_max(&self, groups: &GroupsType) -> Self {
722 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_max(g) })
723 }
724
725 #[cfg(feature = "algorithm_group_by")]
729 pub unsafe fn agg_mean(&self, groups: &GroupsType) -> Self {
730 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_mean(g) })
731 }
732
733 #[cfg(feature = "algorithm_group_by")]
737 pub unsafe fn agg_sum(&self, groups: &GroupsType) -> Self {
738 unsafe { self.as_materialized_series().agg_sum(groups) }.into()
740 }
741
742 #[cfg(feature = "algorithm_group_by")]
746 pub unsafe fn agg_first(&self, groups: &GroupsType) -> Self {
747 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_first(g) })
748 }
749
750 #[cfg(feature = "algorithm_group_by")]
754 pub unsafe fn agg_last(&self, groups: &GroupsType) -> Self {
755 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_last(g) })
756 }
757
758 #[cfg(feature = "algorithm_group_by")]
762 pub unsafe fn agg_n_unique(&self, groups: &GroupsType) -> Self {
763 unsafe { self.as_materialized_series().agg_n_unique(groups) }.into()
765 }
766
767 #[cfg(feature = "algorithm_group_by")]
771 pub unsafe fn agg_quantile(
772 &self,
773 groups: &GroupsType,
774 quantile: f64,
775 method: QuantileMethod,
776 ) -> Self {
777 unsafe {
780 self.as_materialized_series()
781 .agg_quantile(groups, quantile, method)
782 }
783 .into()
784 }
785
786 #[cfg(feature = "algorithm_group_by")]
790 pub unsafe fn agg_median(&self, groups: &GroupsType) -> Self {
791 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_median(g) })
792 }
793
794 #[cfg(feature = "algorithm_group_by")]
798 pub unsafe fn agg_var(&self, groups: &GroupsType, ddof: u8) -> Self {
799 unsafe { self.as_materialized_series().agg_var(groups, ddof) }.into()
801 }
802
803 #[cfg(feature = "algorithm_group_by")]
807 pub unsafe fn agg_std(&self, groups: &GroupsType, ddof: u8) -> Self {
808 unsafe { self.as_materialized_series().agg_std(groups, ddof) }.into()
810 }
811
812 #[cfg(feature = "algorithm_group_by")]
816 pub unsafe fn agg_list(&self, groups: &GroupsType) -> Self {
817 unsafe { self.as_materialized_series().agg_list(groups) }.into()
819 }
820
821 #[cfg(feature = "algorithm_group_by")]
825 pub fn agg_valid_count(&self, groups: &GroupsType) -> Self {
826 unsafe { self.as_materialized_series().agg_valid_count(groups) }.into()
829 }
830
831 #[cfg(feature = "bitwise")]
835 pub fn agg_and(&self, groups: &GroupsType) -> Self {
836 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_and(g) })
837 }
838 #[cfg(feature = "bitwise")]
842 pub fn agg_or(&self, groups: &GroupsType) -> Self {
843 self.agg_with_unit_scalar(groups, |s, g| unsafe { s.agg_or(g) })
844 }
845 #[cfg(feature = "bitwise")]
849 pub fn agg_xor(&self, groups: &GroupsType) -> Self {
850 unsafe { self.as_materialized_series().agg_xor(groups) }.into()
853 }
854
855 pub fn full_null(name: PlSmallStr, size: usize, dtype: &DataType) -> Self {
856 Self::new_scalar(name, Scalar::new(dtype.clone(), AnyValue::Null), size)
857 }
858
859 pub fn is_empty(&self) -> bool {
860 self.len() == 0
861 }
862
863 pub fn reverse(&self) -> Column {
864 match self {
865 Column::Series(s) => s.reverse().into(),
866 Column::Partitioned(s) => s.reverse().into(),
867 Column::Scalar(_) => self.clone(),
868 }
869 }
870
871 pub fn equals(&self, other: &Column) -> bool {
872 self.as_materialized_series()
874 .equals(other.as_materialized_series())
875 }
876
877 pub fn equals_missing(&self, other: &Column) -> bool {
878 self.as_materialized_series()
880 .equals_missing(other.as_materialized_series())
881 }
882
883 pub fn set_sorted_flag(&mut self, sorted: IsSorted) {
884 match self {
886 Column::Series(s) => s.set_sorted_flag(sorted),
887 Column::Partitioned(s) => s.set_sorted_flag(sorted),
888 Column::Scalar(_) => {},
889 }
890 }
891
892 pub fn get_flags(&self) -> StatisticsFlags {
893 match self {
894 Column::Series(s) => s.get_flags(),
895 Column::Partitioned(_) => StatisticsFlags::empty(),
897 Column::Scalar(_) => {
898 StatisticsFlags::IS_SORTED_ASC | StatisticsFlags::CAN_FAST_EXPLODE_LIST
899 },
900 }
901 }
902
903 pub fn set_flags(&mut self, flags: StatisticsFlags) -> bool {
905 match self {
906 Column::Series(s) => {
907 s.set_flags(flags);
908 true
909 },
910 Column::Partitioned(_) => false,
912 Column::Scalar(_) => false,
913 }
914 }
915
916 pub fn vec_hash(
917 &self,
918 build_hasher: PlSeedableRandomStateQuality,
919 buf: &mut Vec<u64>,
920 ) -> PolarsResult<()> {
921 self.as_materialized_series().vec_hash(build_hasher, buf)
923 }
924
925 pub fn vec_hash_combine(
926 &self,
927 build_hasher: PlSeedableRandomStateQuality,
928 hashes: &mut [u64],
929 ) -> PolarsResult<()> {
930 self.as_materialized_series()
932 .vec_hash_combine(build_hasher, hashes)
933 }
934
935 pub fn append(&mut self, other: &Column) -> PolarsResult<&mut Self> {
936 self.into_materialized_series()
938 .append(other.as_materialized_series())?;
939 Ok(self)
940 }
941 pub fn append_owned(&mut self, other: Column) -> PolarsResult<&mut Self> {
942 self.into_materialized_series()
943 .append_owned(other.take_materialized_series())?;
944 Ok(self)
945 }
946
947 pub fn arg_sort(&self, options: SortOptions) -> IdxCa {
948 if self.is_empty() {
949 return IdxCa::from_vec(self.name().clone(), Vec::new());
950 }
951
952 if self.null_count() == self.len() {
953 let values = if options.descending {
955 (0..self.len() as IdxSize).rev().collect()
956 } else {
957 (0..self.len() as IdxSize).collect()
958 };
959
960 return IdxCa::from_vec(self.name().clone(), values);
961 }
962
963 let is_sorted = Some(self.is_sorted_flag());
964 let Some(is_sorted) = is_sorted.filter(|v| !matches!(v, IsSorted::Not)) else {
965 return self.as_materialized_series().arg_sort(options);
966 };
967
968 let is_sorted_dsc = matches!(is_sorted, IsSorted::Descending);
970 let invert = options.descending != is_sorted_dsc;
971
972 let mut values = Vec::with_capacity(self.len());
973
974 #[inline(never)]
975 fn extend(
976 start: IdxSize,
977 end: IdxSize,
978 slf: &Column,
979 values: &mut Vec<IdxSize>,
980 is_only_nulls: bool,
981 invert: bool,
982 maintain_order: bool,
983 ) {
984 debug_assert!(start <= end);
985 debug_assert!(start as usize <= slf.len());
986 debug_assert!(end as usize <= slf.len());
987
988 if !invert || is_only_nulls {
989 values.extend(start..end);
990 return;
991 }
992
993 if !maintain_order {
995 values.extend((start..end).rev());
996 return;
997 }
998
999 let arg_unique = slf
1005 .slice(start as i64, (end - start) as usize)
1006 .arg_unique()
1007 .unwrap();
1008
1009 assert!(!arg_unique.has_nulls());
1010
1011 let num_unique = arg_unique.len();
1012
1013 if num_unique == (end - start) as usize {
1015 values.extend((start..end).rev());
1016 return;
1017 }
1018
1019 if num_unique == 1 {
1020 values.extend(start..end);
1021 return;
1022 }
1023
1024 let mut prev_idx = end - start;
1025 for chunk in arg_unique.downcast_iter() {
1026 for &idx in chunk.values().as_slice().iter().rev() {
1027 values.extend(start + idx..start + prev_idx);
1028 prev_idx = idx;
1029 }
1030 }
1031 }
1032 macro_rules! extend {
1033 ($start:expr, $end:expr) => {
1034 extend!($start, $end, is_only_nulls = false);
1035 };
1036 ($start:expr, $end:expr, is_only_nulls = $is_only_nulls:expr) => {
1037 extend(
1038 $start,
1039 $end,
1040 self,
1041 &mut values,
1042 $is_only_nulls,
1043 invert,
1044 options.maintain_order,
1045 );
1046 };
1047 }
1048
1049 let length = self.len() as IdxSize;
1050 let null_count = self.null_count() as IdxSize;
1051
1052 if null_count == 0 {
1053 extend!(0, length);
1054 } else {
1055 let has_nulls_last = self.get(self.len() - 1).unwrap().is_null();
1056 match (options.nulls_last, has_nulls_last) {
1057 (true, true) => {
1058 extend!(0, length - null_count);
1060 extend!(length - null_count, length, is_only_nulls = true);
1061 },
1062 (true, false) => {
1063 extend!(null_count, length);
1065 extend!(0, null_count, is_only_nulls = true);
1066 },
1067 (false, true) => {
1068 extend!(length - null_count, length, is_only_nulls = true);
1070 extend!(0, length - null_count);
1071 },
1072 (false, false) => {
1073 extend!(0, null_count, is_only_nulls = true);
1075 extend!(null_count, length);
1076 },
1077 }
1078 }
1079
1080 if let Some(limit) = options.limit {
1083 let limit = limit.min(length);
1084 values.truncate(limit as usize);
1085 }
1086
1087 IdxCa::from_vec(self.name().clone(), values)
1088 }
1089
1090 pub fn arg_sort_multiple(
1091 &self,
1092 by: &[Column],
1093 options: &SortMultipleOptions,
1094 ) -> PolarsResult<IdxCa> {
1095 self.as_materialized_series().arg_sort_multiple(by, options)
1097 }
1098
1099 pub fn arg_unique(&self) -> PolarsResult<IdxCa> {
1100 match self {
1101 Column::Scalar(s) => Ok(IdxCa::new_vec(s.name().clone(), vec![0])),
1102 _ => self.as_materialized_series().arg_unique(),
1103 }
1104 }
1105
1106 pub fn bit_repr(&self) -> Option<BitRepr> {
1107 self.as_materialized_series().bit_repr()
1109 }
1110
1111 pub fn into_frame(self) -> DataFrame {
1112 unsafe { DataFrame::new_no_checks(self.len(), vec![self]) }
1114 }
1115
1116 pub fn extend(&mut self, other: &Column) -> PolarsResult<&mut Self> {
1117 self.into_materialized_series()
1119 .extend(other.as_materialized_series())?;
1120 Ok(self)
1121 }
1122
1123 pub fn rechunk(&self) -> Column {
1124 match self {
1125 Column::Series(s) => s.rechunk().into(),
1126 Column::Partitioned(s) => {
1127 if let Some(s) = s.lazy_as_materialized_series() {
1128 debug_assert_eq!(s.n_chunks(), 1)
1130 }
1131 self.clone()
1132 },
1133 Column::Scalar(s) => {
1134 if s.lazy_as_materialized_series()
1135 .filter(|x| x.n_chunks() > 1)
1136 .is_some()
1137 {
1138 Column::Scalar(ScalarColumn::new(
1139 s.name().clone(),
1140 s.scalar().clone(),
1141 s.len(),
1142 ))
1143 } else {
1144 self.clone()
1145 }
1146 },
1147 }
1148 }
1149
1150 pub fn explode(&self) -> PolarsResult<Column> {
1151 self.as_materialized_series().explode().map(Column::from)
1152 }
1153 pub fn implode(&self) -> PolarsResult<ListChunked> {
1154 self.as_materialized_series().implode()
1155 }
1156
1157 pub fn fill_null(&self, strategy: FillNullStrategy) -> PolarsResult<Self> {
1158 self.as_materialized_series()
1160 .fill_null(strategy)
1161 .map(Column::from)
1162 }
1163
1164 pub fn divide(&self, rhs: &Column) -> PolarsResult<Self> {
1165 self.as_materialized_series()
1167 .divide(rhs.as_materialized_series())
1168 .map(Column::from)
1169 }
1170
1171 pub fn shift(&self, periods: i64) -> Column {
1172 self.as_materialized_series().shift(periods).into()
1174 }
1175
1176 #[cfg(feature = "zip_with")]
1177 pub fn zip_with(&self, mask: &BooleanChunked, other: &Self) -> PolarsResult<Self> {
1178 self.as_materialized_series()
1180 .zip_with(mask, other.as_materialized_series())
1181 .map(Self::from)
1182 }
1183
1184 #[cfg(feature = "zip_with")]
1185 pub fn zip_with_same_type(
1186 &self,
1187 mask: &ChunkedArray<BooleanType>,
1188 other: &Column,
1189 ) -> PolarsResult<Column> {
1190 self.as_materialized_series()
1192 .zip_with_same_type(mask, other.as_materialized_series())
1193 .map(Column::from)
1194 }
1195
1196 pub fn drop_nulls(&self) -> Column {
1197 match self {
1198 Column::Series(s) => s.drop_nulls().into_column(),
1199 Column::Partitioned(s) => s.as_materialized_series().drop_nulls().into_column(),
1201 Column::Scalar(s) => s.drop_nulls().into_column(),
1202 }
1203 }
1204
1205 pub fn as_list(&self) -> ListChunked {
1207 self.as_materialized_series().as_list()
1210 }
1211
1212 pub fn is_sorted_flag(&self) -> IsSorted {
1213 match self {
1214 Column::Series(s) => s.is_sorted_flag(),
1215 Column::Partitioned(s) => s.partitions().is_sorted_flag(),
1216 Column::Scalar(_) => IsSorted::Ascending,
1217 }
1218 }
1219
1220 pub fn unique(&self) -> PolarsResult<Column> {
1221 match self {
1222 Column::Series(s) => s.unique().map(Column::from),
1223 Column::Partitioned(s) => s.as_materialized_series().unique().map(Column::from),
1225 Column::Scalar(s) => {
1226 _ = s.as_single_value_series().unique()?;
1227 if s.is_empty() {
1228 return Ok(s.clone().into_column());
1229 }
1230
1231 Ok(s.resize(1).into_column())
1232 },
1233 }
1234 }
1235 pub fn unique_stable(&self) -> PolarsResult<Column> {
1236 match self {
1237 Column::Series(s) => s.unique_stable().map(Column::from),
1238 Column::Partitioned(s) => s.as_materialized_series().unique_stable().map(Column::from),
1240 Column::Scalar(s) => {
1241 _ = s.as_single_value_series().unique_stable()?;
1242 if s.is_empty() {
1243 return Ok(s.clone().into_column());
1244 }
1245
1246 Ok(s.resize(1).into_column())
1247 },
1248 }
1249 }
1250
1251 pub fn reshape_list(&self, dimensions: &[ReshapeDimension]) -> PolarsResult<Self> {
1252 self.as_materialized_series()
1254 .reshape_list(dimensions)
1255 .map(Self::from)
1256 }
1257
1258 #[cfg(feature = "dtype-array")]
1259 pub fn reshape_array(&self, dimensions: &[ReshapeDimension]) -> PolarsResult<Self> {
1260 self.as_materialized_series()
1262 .reshape_array(dimensions)
1263 .map(Self::from)
1264 }
1265
1266 pub fn sort(&self, sort_options: SortOptions) -> PolarsResult<Self> {
1267 self.as_materialized_series()
1269 .sort(sort_options)
1270 .map(Self::from)
1271 }
1272
1273 pub fn filter(&self, filter: &BooleanChunked) -> PolarsResult<Self> {
1274 match self {
1275 Column::Series(s) => s.filter(filter).map(Column::from),
1276 Column::Partitioned(s) => s.as_materialized_series().filter(filter).map(Column::from),
1277 Column::Scalar(s) => {
1278 if s.is_empty() {
1279 return Ok(s.clone().into_column());
1280 }
1281
1282 if filter.len() == 1 {
1284 return match filter.get(0) {
1285 Some(true) => Ok(s.clone().into_column()),
1286 _ => Ok(s.resize(0).into_column()),
1287 };
1288 }
1289
1290 Ok(s.resize(filter.sum().unwrap() as usize).into_column())
1291 },
1292 }
1293 }
1294
1295 #[cfg(feature = "random")]
1296 pub fn shuffle(&self, seed: Option<u64>) -> Self {
1297 self.as_materialized_series().shuffle(seed).into()
1299 }
1300
1301 #[cfg(feature = "random")]
1302 pub fn sample_frac(
1303 &self,
1304 frac: f64,
1305 with_replacement: bool,
1306 shuffle: bool,
1307 seed: Option<u64>,
1308 ) -> PolarsResult<Self> {
1309 self.as_materialized_series()
1310 .sample_frac(frac, with_replacement, shuffle, seed)
1311 .map(Self::from)
1312 }
1313
1314 #[cfg(feature = "random")]
1315 pub fn sample_n(
1316 &self,
1317 n: usize,
1318 with_replacement: bool,
1319 shuffle: bool,
1320 seed: Option<u64>,
1321 ) -> PolarsResult<Self> {
1322 self.as_materialized_series()
1323 .sample_n(n, with_replacement, shuffle, seed)
1324 .map(Self::from)
1325 }
1326
1327 pub fn gather_every(&self, n: usize, offset: usize) -> Column {
1328 if self.len().saturating_sub(offset) == 0 {
1329 return self.clear();
1330 }
1331
1332 match self {
1333 Column::Series(s) => s.gather_every(n, offset).into(),
1334 Column::Partitioned(s) => s.as_materialized_series().gather_every(n, offset).into(),
1335 Column::Scalar(s) => {
1336 let total = s.len() - offset;
1337 s.resize(1 + (total - 1) / n).into()
1338 },
1339 }
1340 }
1341
1342 pub fn extend_constant(&self, value: AnyValue, n: usize) -> PolarsResult<Self> {
1343 if self.is_empty() {
1344 return Ok(Self::new_scalar(
1345 self.name().clone(),
1346 Scalar::new(self.dtype().clone(), value.into_static()),
1347 n,
1348 ));
1349 }
1350
1351 match self {
1352 Column::Series(s) => s.extend_constant(value, n).map(Column::from),
1353 Column::Partitioned(s) => s.extend_constant(value, n).map(Column::from),
1354 Column::Scalar(s) => {
1355 if s.scalar().as_any_value() == value {
1356 Ok(s.resize(s.len() + n).into())
1357 } else {
1358 s.as_materialized_series()
1359 .extend_constant(value, n)
1360 .map(Column::from)
1361 }
1362 },
1363 }
1364 }
1365
1366 pub fn is_finite(&self) -> PolarsResult<BooleanChunked> {
1367 self.try_map_unary_elementwise_to_bool(|s| s.is_finite())
1368 }
1369 pub fn is_infinite(&self) -> PolarsResult<BooleanChunked> {
1370 self.try_map_unary_elementwise_to_bool(|s| s.is_infinite())
1371 }
1372 pub fn is_nan(&self) -> PolarsResult<BooleanChunked> {
1373 self.try_map_unary_elementwise_to_bool(|s| s.is_nan())
1374 }
1375 pub fn is_not_nan(&self) -> PolarsResult<BooleanChunked> {
1376 self.try_map_unary_elementwise_to_bool(|s| s.is_not_nan())
1377 }
1378
1379 pub fn wrapping_trunc_div_scalar<T>(&self, rhs: T) -> Self
1380 where
1381 T: Num + NumCast,
1382 {
1383 self.as_materialized_series()
1385 .wrapping_trunc_div_scalar(rhs)
1386 .into()
1387 }
1388
1389 pub fn product(&self) -> PolarsResult<Scalar> {
1390 self.as_materialized_series().product()
1392 }
1393
1394 pub fn phys_iter(&self) -> SeriesPhysIter<'_> {
1395 self.as_materialized_series().phys_iter()
1397 }
1398
1399 #[inline]
1400 pub fn get(&self, index: usize) -> PolarsResult<AnyValue> {
1401 polars_ensure!(index < self.len(), oob = index, self.len());
1402
1403 Ok(unsafe { self.get_unchecked(index) })
1405 }
1406 #[inline(always)]
1410 pub unsafe fn get_unchecked(&self, index: usize) -> AnyValue {
1411 debug_assert!(index < self.len());
1412
1413 match self {
1414 Column::Series(s) => unsafe { s.get_unchecked(index) },
1415 Column::Partitioned(s) => unsafe { s.get_unchecked(index) },
1416 Column::Scalar(s) => s.scalar().as_any_value(),
1417 }
1418 }
1419
1420 #[cfg(feature = "object")]
1421 pub fn get_object(
1422 &self,
1423 index: usize,
1424 ) -> Option<&dyn crate::chunked_array::object::PolarsObjectSafe> {
1425 self.as_materialized_series().get_object(index)
1426 }
1427
1428 pub fn bitand(&self, rhs: &Self) -> PolarsResult<Self> {
1429 self.try_apply_broadcasting_binary_elementwise(rhs, |l, r| l & r)
1430 }
1431 pub fn bitor(&self, rhs: &Self) -> PolarsResult<Self> {
1432 self.try_apply_broadcasting_binary_elementwise(rhs, |l, r| l | r)
1433 }
1434 pub fn bitxor(&self, rhs: &Self) -> PolarsResult<Self> {
1435 self.try_apply_broadcasting_binary_elementwise(rhs, |l, r| l ^ r)
1436 }
1437
1438 pub fn try_add_owned(self, other: Self) -> PolarsResult<Self> {
1439 match (self, other) {
1440 (Column::Series(lhs), Column::Series(rhs)) => {
1441 lhs.take().try_add_owned(rhs.take()).map(Column::from)
1442 },
1443 (lhs, rhs) => lhs + rhs,
1444 }
1445 }
1446 pub fn try_sub_owned(self, other: Self) -> PolarsResult<Self> {
1447 match (self, other) {
1448 (Column::Series(lhs), Column::Series(rhs)) => {
1449 lhs.take().try_sub_owned(rhs.take()).map(Column::from)
1450 },
1451 (lhs, rhs) => lhs - rhs,
1452 }
1453 }
1454 pub fn try_mul_owned(self, other: Self) -> PolarsResult<Self> {
1455 match (self, other) {
1456 (Column::Series(lhs), Column::Series(rhs)) => {
1457 lhs.take().try_mul_owned(rhs.take()).map(Column::from)
1458 },
1459 (lhs, rhs) => lhs * rhs,
1460 }
1461 }
1462
1463 pub(crate) fn str_value(&self, index: usize) -> PolarsResult<Cow<str>> {
1464 Ok(self.get(index)?.str_value())
1465 }
1466
1467 pub fn min_reduce(&self) -> PolarsResult<Scalar> {
1468 match self {
1469 Column::Series(s) => s.min_reduce(),
1470 Column::Partitioned(s) => s.min_reduce(),
1471 Column::Scalar(s) => {
1472 s.as_single_value_series().min_reduce()
1475 },
1476 }
1477 }
1478 pub fn max_reduce(&self) -> PolarsResult<Scalar> {
1479 match self {
1480 Column::Series(s) => s.max_reduce(),
1481 Column::Partitioned(s) => s.max_reduce(),
1482 Column::Scalar(s) => {
1483 s.as_single_value_series().max_reduce()
1486 },
1487 }
1488 }
1489 pub fn median_reduce(&self) -> PolarsResult<Scalar> {
1490 match self {
1491 Column::Series(s) => s.median_reduce(),
1492 Column::Partitioned(s) => s.as_materialized_series().median_reduce(),
1493 Column::Scalar(s) => {
1494 s.as_single_value_series().median_reduce()
1497 },
1498 }
1499 }
1500 pub fn mean_reduce(&self) -> Scalar {
1501 match self {
1502 Column::Series(s) => s.mean_reduce(),
1503 Column::Partitioned(s) => s.as_materialized_series().mean_reduce(),
1504 Column::Scalar(s) => {
1505 s.as_single_value_series().mean_reduce()
1508 },
1509 }
1510 }
1511 pub fn std_reduce(&self, ddof: u8) -> PolarsResult<Scalar> {
1512 match self {
1513 Column::Series(s) => s.std_reduce(ddof),
1514 Column::Partitioned(s) => s.as_materialized_series().std_reduce(ddof),
1515 Column::Scalar(s) => {
1516 s.as_single_value_series().std_reduce(ddof)
1519 },
1520 }
1521 }
1522 pub fn var_reduce(&self, ddof: u8) -> PolarsResult<Scalar> {
1523 match self {
1524 Column::Series(s) => s.var_reduce(ddof),
1525 Column::Partitioned(s) => s.as_materialized_series().var_reduce(ddof),
1526 Column::Scalar(s) => {
1527 s.as_single_value_series().var_reduce(ddof)
1530 },
1531 }
1532 }
1533 pub fn sum_reduce(&self) -> PolarsResult<Scalar> {
1534 self.as_materialized_series().sum_reduce()
1537 }
1538 pub fn and_reduce(&self) -> PolarsResult<Scalar> {
1539 match self {
1540 Column::Series(s) => s.and_reduce(),
1541 Column::Partitioned(s) => s.and_reduce(),
1542 Column::Scalar(s) => {
1543 s.as_single_value_series().and_reduce()
1546 },
1547 }
1548 }
1549 pub fn or_reduce(&self) -> PolarsResult<Scalar> {
1550 match self {
1551 Column::Series(s) => s.or_reduce(),
1552 Column::Partitioned(s) => s.or_reduce(),
1553 Column::Scalar(s) => {
1554 s.as_single_value_series().or_reduce()
1557 },
1558 }
1559 }
1560 pub fn xor_reduce(&self) -> PolarsResult<Scalar> {
1561 match self {
1562 Column::Series(s) => s.xor_reduce(),
1563 Column::Partitioned(s) => s.as_materialized_series().xor_reduce(),
1565 Column::Scalar(s) => {
1566 s.as_n_values_series(2 - s.len() % 2).xor_reduce()
1573 },
1574 }
1575 }
1576 pub fn n_unique(&self) -> PolarsResult<usize> {
1577 match self {
1578 Column::Series(s) => s.n_unique(),
1579 Column::Partitioned(s) => s.partitions().n_unique(),
1580 Column::Scalar(s) => s.as_single_value_series().n_unique(),
1581 }
1582 }
1583 pub fn quantile_reduce(&self, quantile: f64, method: QuantileMethod) -> PolarsResult<Scalar> {
1584 self.as_materialized_series()
1585 .quantile_reduce(quantile, method)
1586 }
1587
1588 pub(crate) fn estimated_size(&self) -> usize {
1589 self.as_materialized_series().estimated_size()
1591 }
1592
1593 pub fn sort_with(&self, options: SortOptions) -> PolarsResult<Self> {
1594 match self {
1595 Column::Series(s) => s.sort_with(options).map(Self::from),
1596 Column::Partitioned(s) => s
1598 .as_materialized_series()
1599 .sort_with(options)
1600 .map(Self::from),
1601 Column::Scalar(s) => {
1602 _ = s.as_single_value_series().sort_with(options)?;
1604
1605 Ok(self.clone())
1606 },
1607 }
1608 }
1609
1610 pub fn map_unary_elementwise_to_bool(
1611 &self,
1612 f: impl Fn(&Series) -> BooleanChunked,
1613 ) -> BooleanChunked {
1614 self.try_map_unary_elementwise_to_bool(|s| Ok(f(s)))
1615 .unwrap()
1616 }
1617 pub fn try_map_unary_elementwise_to_bool(
1618 &self,
1619 f: impl Fn(&Series) -> PolarsResult<BooleanChunked>,
1620 ) -> PolarsResult<BooleanChunked> {
1621 match self {
1622 Column::Series(s) => f(s),
1623 Column::Partitioned(s) => f(s.as_materialized_series()),
1624 Column::Scalar(s) => Ok(f(&s.as_single_value_series())?.new_from_index(0, s.len())),
1625 }
1626 }
1627
1628 pub fn apply_unary_elementwise(&self, f: impl Fn(&Series) -> Series) -> Column {
1629 self.try_apply_unary_elementwise(|s| Ok(f(s))).unwrap()
1630 }
1631 pub fn try_apply_unary_elementwise(
1632 &self,
1633 f: impl Fn(&Series) -> PolarsResult<Series>,
1634 ) -> PolarsResult<Column> {
1635 match self {
1636 Column::Series(s) => f(s).map(Column::from),
1637 Column::Partitioned(s) => s.try_apply_unary_elementwise(f).map(Self::from),
1638 Column::Scalar(s) => Ok(ScalarColumn::from_single_value_series(
1639 f(&s.as_single_value_series())?,
1640 s.len(),
1641 )
1642 .into()),
1643 }
1644 }
1645
1646 pub fn apply_broadcasting_binary_elementwise(
1647 &self,
1648 other: &Self,
1649 op: impl Fn(&Series, &Series) -> Series,
1650 ) -> PolarsResult<Column> {
1651 self.try_apply_broadcasting_binary_elementwise(other, |lhs, rhs| Ok(op(lhs, rhs)))
1652 }
1653 pub fn try_apply_broadcasting_binary_elementwise(
1654 &self,
1655 other: &Self,
1656 op: impl Fn(&Series, &Series) -> PolarsResult<Series>,
1657 ) -> PolarsResult<Column> {
1658 fn output_length(a: &Column, b: &Column) -> PolarsResult<usize> {
1659 match (a.len(), b.len()) {
1660 (1, o) | (o, 1) => Ok(o),
1662 (a, b) if a == b => Ok(a),
1664 (a, b) => {
1666 polars_bail!(InvalidOperation: "cannot do a binary operation on columns of different lengths: got {} and {}", a, b)
1667 },
1668 }
1669 }
1670
1671 let length = output_length(self, other)?;
1673 match (self, other) {
1674 (Column::Series(lhs), Column::Series(rhs)) => op(lhs, rhs).map(Column::from),
1675 (Column::Series(lhs), Column::Scalar(rhs)) => {
1676 op(lhs, &rhs.as_single_value_series()).map(Column::from)
1677 },
1678 (Column::Scalar(lhs), Column::Series(rhs)) => {
1679 op(&lhs.as_single_value_series(), rhs).map(Column::from)
1680 },
1681 (Column::Scalar(lhs), Column::Scalar(rhs)) => {
1682 let lhs = lhs.as_single_value_series();
1683 let rhs = rhs.as_single_value_series();
1684
1685 Ok(ScalarColumn::from_single_value_series(op(&lhs, &rhs)?, length).into_column())
1686 },
1687 (lhs, rhs) => {
1689 op(lhs.as_materialized_series(), rhs.as_materialized_series()).map(Column::from)
1690 },
1691 }
1692 }
1693
1694 pub fn apply_binary_elementwise(
1695 &self,
1696 other: &Self,
1697 f: impl Fn(&Series, &Series) -> Series,
1698 f_lb: impl Fn(&Scalar, &Series) -> Series,
1699 f_rb: impl Fn(&Series, &Scalar) -> Series,
1700 ) -> Column {
1701 self.try_apply_binary_elementwise(
1702 other,
1703 |lhs, rhs| Ok(f(lhs, rhs)),
1704 |lhs, rhs| Ok(f_lb(lhs, rhs)),
1705 |lhs, rhs| Ok(f_rb(lhs, rhs)),
1706 )
1707 .unwrap()
1708 }
1709 pub fn try_apply_binary_elementwise(
1710 &self,
1711 other: &Self,
1712 f: impl Fn(&Series, &Series) -> PolarsResult<Series>,
1713 f_lb: impl Fn(&Scalar, &Series) -> PolarsResult<Series>,
1714 f_rb: impl Fn(&Series, &Scalar) -> PolarsResult<Series>,
1715 ) -> PolarsResult<Column> {
1716 debug_assert_eq!(self.len(), other.len());
1717
1718 match (self, other) {
1719 (Column::Series(lhs), Column::Series(rhs)) => f(lhs, rhs).map(Column::from),
1720 (Column::Series(lhs), Column::Scalar(rhs)) => f_rb(lhs, rhs.scalar()).map(Column::from),
1721 (Column::Scalar(lhs), Column::Series(rhs)) => f_lb(lhs.scalar(), rhs).map(Column::from),
1722 (Column::Scalar(lhs), Column::Scalar(rhs)) => {
1723 let lhs = lhs.as_single_value_series();
1724 let rhs = rhs.as_single_value_series();
1725
1726 Ok(
1727 ScalarColumn::from_single_value_series(f(&lhs, &rhs)?, self.len())
1728 .into_column(),
1729 )
1730 },
1731 (lhs, rhs) => {
1733 f(lhs.as_materialized_series(), rhs.as_materialized_series()).map(Column::from)
1734 },
1735 }
1736 }
1737
1738 #[cfg(feature = "approx_unique")]
1739 pub fn approx_n_unique(&self) -> PolarsResult<IdxSize> {
1740 match self {
1741 Column::Series(s) => s.approx_n_unique(),
1742 Column::Partitioned(s) => s.as_materialized_series().approx_n_unique(),
1744 Column::Scalar(s) => {
1745 s.as_single_value_series().approx_n_unique()?;
1747 Ok(1)
1748 },
1749 }
1750 }
1751
1752 pub fn n_chunks(&self) -> usize {
1753 match self {
1754 Column::Series(s) => s.n_chunks(),
1755 Column::Scalar(s) => s.lazy_as_materialized_series().map_or(1, |x| x.n_chunks()),
1756 Column::Partitioned(s) => {
1757 if let Some(s) = s.lazy_as_materialized_series() {
1758 debug_assert_eq!(s.n_chunks(), 1)
1760 }
1761 1
1762 },
1763 }
1764 }
1765
1766 #[expect(clippy::wrong_self_convention)]
1767 pub(crate) fn into_total_ord_inner<'a>(&'a self) -> Box<dyn TotalOrdInner + 'a> {
1768 self.as_materialized_series().into_total_ord_inner()
1770 }
1771 #[expect(unused, clippy::wrong_self_convention)]
1772 pub(crate) fn into_total_eq_inner<'a>(&'a self) -> Box<dyn TotalEqInner + 'a> {
1773 self.as_materialized_series().into_total_eq_inner()
1775 }
1776
1777 pub fn rechunk_to_arrow(self, compat_level: CompatLevel) -> Box<dyn Array> {
1778 let mut series = self.take_materialized_series();
1780 if series.n_chunks() > 1 {
1781 series = series.rechunk();
1782 }
1783 series.to_arrow(0, compat_level)
1784 }
1785}
1786
1787impl Default for Column {
1788 fn default() -> Self {
1789 Self::new_scalar(
1790 PlSmallStr::EMPTY,
1791 Scalar::new(DataType::Int64, AnyValue::Null),
1792 0,
1793 )
1794 }
1795}
1796
1797impl PartialEq for Column {
1798 fn eq(&self, other: &Self) -> bool {
1799 self.as_materialized_series()
1801 .eq(other.as_materialized_series())
1802 }
1803}
1804
1805impl From<Series> for Column {
1806 #[inline]
1807 fn from(series: Series) -> Self {
1808 if series.len() == 1 {
1811 return Self::Scalar(ScalarColumn::unit_scalar_from_series(series));
1812 }
1813
1814 Self::Series(SeriesColumn::new(series))
1815 }
1816}
1817
1818impl<T: IntoSeries> IntoColumn for T {
1819 #[inline]
1820 fn into_column(self) -> Column {
1821 self.into_series().into()
1822 }
1823}
1824
1825impl IntoColumn for Column {
1826 #[inline(always)]
1827 fn into_column(self) -> Column {
1828 self
1829 }
1830}
1831
1832#[derive(Clone)]
1837#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1838#[cfg_attr(feature = "serde", serde(into = "Series"))]
1839struct _SerdeSeries(Series);
1840
1841impl From<Column> for _SerdeSeries {
1842 #[inline]
1843 fn from(value: Column) -> Self {
1844 Self(value.take_materialized_series())
1845 }
1846}
1847
1848impl From<_SerdeSeries> for Series {
1849 #[inline]
1850 fn from(value: _SerdeSeries) -> Self {
1851 value.0
1852 }
1853}