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