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