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 #[inline]
250 pub fn as_scalar_column(&self) -> Option<&ScalarColumn> {
251 match self {
252 Column::Scalar(s) => Some(s),
253 _ => None,
254 }
255 }
256 #[inline]
257 pub fn as_scalar_column_mut(&mut self) -> Option<&mut ScalarColumn> {
258 match self {
259 Column::Scalar(s) => Some(s),
260 _ => None,
261 }
262 }
263
264 pub fn try_bool(&self) -> Option<&BooleanChunked> {
266 self.as_materialized_series().try_bool()
267 }
268 pub fn try_i8(&self) -> Option<&Int8Chunked> {
269 self.as_materialized_series().try_i8()
270 }
271 pub fn try_i16(&self) -> Option<&Int16Chunked> {
272 self.as_materialized_series().try_i16()
273 }
274 pub fn try_i32(&self) -> Option<&Int32Chunked> {
275 self.as_materialized_series().try_i32()
276 }
277 pub fn try_i64(&self) -> Option<&Int64Chunked> {
278 self.as_materialized_series().try_i64()
279 }
280 pub fn try_u8(&self) -> Option<&UInt8Chunked> {
281 self.as_materialized_series().try_u8()
282 }
283 pub fn try_u16(&self) -> Option<&UInt16Chunked> {
284 self.as_materialized_series().try_u16()
285 }
286 pub fn try_u32(&self) -> Option<&UInt32Chunked> {
287 self.as_materialized_series().try_u32()
288 }
289 pub fn try_u64(&self) -> Option<&UInt64Chunked> {
290 self.as_materialized_series().try_u64()
291 }
292 #[cfg(feature = "dtype-u128")]
293 pub fn try_u128(&self) -> Option<&UInt128Chunked> {
294 self.as_materialized_series().try_u128()
295 }
296 #[cfg(feature = "dtype-f16")]
297 pub fn try_f16(&self) -> Option<&Float16Chunked> {
298 self.as_materialized_series().try_f16()
299 }
300 pub fn try_f32(&self) -> Option<&Float32Chunked> {
301 self.as_materialized_series().try_f32()
302 }
303 pub fn try_f64(&self) -> Option<&Float64Chunked> {
304 self.as_materialized_series().try_f64()
305 }
306 pub fn try_str(&self) -> Option<&StringChunked> {
307 self.as_materialized_series().try_str()
308 }
309 pub fn try_list(&self) -> Option<&ListChunked> {
310 self.as_materialized_series().try_list()
311 }
312 pub fn try_binary(&self) -> Option<&BinaryChunked> {
313 self.as_materialized_series().try_binary()
314 }
315 pub fn try_idx(&self) -> Option<&IdxCa> {
316 self.as_materialized_series().try_idx()
317 }
318 pub fn try_binary_offset(&self) -> Option<&BinaryOffsetChunked> {
319 self.as_materialized_series().try_binary_offset()
320 }
321 #[cfg(feature = "dtype-datetime")]
322 pub fn try_datetime(&self) -> Option<&DatetimeChunked> {
323 self.as_materialized_series().try_datetime()
324 }
325 #[cfg(feature = "dtype-struct")]
326 pub fn try_struct(&self) -> Option<&StructChunked> {
327 self.as_materialized_series().try_struct()
328 }
329 #[cfg(feature = "dtype-decimal")]
330 pub fn try_decimal(&self) -> Option<&DecimalChunked> {
331 self.as_materialized_series().try_decimal()
332 }
333 #[cfg(feature = "dtype-array")]
334 pub fn try_array(&self) -> Option<&ArrayChunked> {
335 self.as_materialized_series().try_array()
336 }
337 #[cfg(feature = "dtype-categorical")]
338 pub fn try_cat<T: PolarsCategoricalType>(&self) -> Option<&CategoricalChunked<T>> {
339 self.as_materialized_series().try_cat::<T>()
340 }
341 #[cfg(feature = "dtype-categorical")]
342 pub fn try_cat8(&self) -> Option<&Categorical8Chunked> {
343 self.as_materialized_series().try_cat8()
344 }
345 #[cfg(feature = "dtype-categorical")]
346 pub fn try_cat16(&self) -> Option<&Categorical16Chunked> {
347 self.as_materialized_series().try_cat16()
348 }
349 #[cfg(feature = "dtype-categorical")]
350 pub fn try_cat32(&self) -> Option<&Categorical32Chunked> {
351 self.as_materialized_series().try_cat32()
352 }
353 #[cfg(feature = "dtype-date")]
354 pub fn try_date(&self) -> Option<&DateChunked> {
355 self.as_materialized_series().try_date()
356 }
357 #[cfg(feature = "dtype-duration")]
358 pub fn try_duration(&self) -> Option<&DurationChunked> {
359 self.as_materialized_series().try_duration()
360 }
361
362 pub fn bool(&self) -> PolarsResult<&BooleanChunked> {
364 self.as_materialized_series().bool()
365 }
366 pub fn i8(&self) -> PolarsResult<&Int8Chunked> {
367 self.as_materialized_series().i8()
368 }
369 pub fn i16(&self) -> PolarsResult<&Int16Chunked> {
370 self.as_materialized_series().i16()
371 }
372 pub fn i32(&self) -> PolarsResult<&Int32Chunked> {
373 self.as_materialized_series().i32()
374 }
375 pub fn i64(&self) -> PolarsResult<&Int64Chunked> {
376 self.as_materialized_series().i64()
377 }
378 #[cfg(feature = "dtype-i128")]
379 pub fn i128(&self) -> PolarsResult<&Int128Chunked> {
380 self.as_materialized_series().i128()
381 }
382 pub fn u8(&self) -> PolarsResult<&UInt8Chunked> {
383 self.as_materialized_series().u8()
384 }
385 pub fn u16(&self) -> PolarsResult<&UInt16Chunked> {
386 self.as_materialized_series().u16()
387 }
388 pub fn u32(&self) -> PolarsResult<&UInt32Chunked> {
389 self.as_materialized_series().u32()
390 }
391 pub fn u64(&self) -> PolarsResult<&UInt64Chunked> {
392 self.as_materialized_series().u64()
393 }
394 #[cfg(feature = "dtype-u128")]
395 pub fn u128(&self) -> PolarsResult<&UInt128Chunked> {
396 self.as_materialized_series().u128()
397 }
398 #[cfg(feature = "dtype-f16")]
399 pub fn f16(&self) -> PolarsResult<&Float16Chunked> {
400 self.as_materialized_series().f16()
401 }
402 pub fn f32(&self) -> PolarsResult<&Float32Chunked> {
403 self.as_materialized_series().f32()
404 }
405 pub fn f64(&self) -> PolarsResult<&Float64Chunked> {
406 self.as_materialized_series().f64()
407 }
408 pub fn str(&self) -> PolarsResult<&StringChunked> {
409 self.as_materialized_series().str()
410 }
411 pub fn list(&self) -> PolarsResult<&ListChunked> {
412 self.as_materialized_series().list()
413 }
414 pub fn binary(&self) -> PolarsResult<&BinaryChunked> {
415 self.as_materialized_series().binary()
416 }
417 pub fn idx(&self) -> PolarsResult<&IdxCa> {
418 self.as_materialized_series().idx()
419 }
420 pub fn binary_offset(&self) -> PolarsResult<&BinaryOffsetChunked> {
421 self.as_materialized_series().binary_offset()
422 }
423 #[cfg(feature = "dtype-datetime")]
424 pub fn datetime(&self) -> PolarsResult<&DatetimeChunked> {
425 self.as_materialized_series().datetime()
426 }
427 #[cfg(feature = "dtype-struct")]
428 pub fn struct_(&self) -> PolarsResult<&StructChunked> {
429 self.as_materialized_series().struct_()
430 }
431 #[cfg(feature = "dtype-decimal")]
432 pub fn decimal(&self) -> PolarsResult<&DecimalChunked> {
433 self.as_materialized_series().decimal()
434 }
435 #[cfg(feature = "dtype-array")]
436 pub fn array(&self) -> PolarsResult<&ArrayChunked> {
437 self.as_materialized_series().array()
438 }
439 #[cfg(feature = "dtype-categorical")]
440 pub fn cat<T: PolarsCategoricalType>(&self) -> PolarsResult<&CategoricalChunked<T>> {
441 self.as_materialized_series().cat::<T>()
442 }
443 #[cfg(feature = "dtype-categorical")]
444 pub fn cat8(&self) -> PolarsResult<&Categorical8Chunked> {
445 self.as_materialized_series().cat8()
446 }
447 #[cfg(feature = "dtype-categorical")]
448 pub fn cat16(&self) -> PolarsResult<&Categorical16Chunked> {
449 self.as_materialized_series().cat16()
450 }
451 #[cfg(feature = "dtype-categorical")]
452 pub fn cat32(&self) -> PolarsResult<&Categorical32Chunked> {
453 self.as_materialized_series().cat32()
454 }
455 #[cfg(feature = "dtype-date")]
456 pub fn date(&self) -> PolarsResult<&DateChunked> {
457 self.as_materialized_series().date()
458 }
459 #[cfg(feature = "dtype-duration")]
460 pub fn duration(&self) -> PolarsResult<&DurationChunked> {
461 self.as_materialized_series().duration()
462 }
463
464 pub fn cast_with_options(&self, dtype: &DataType, options: CastOptions) -> PolarsResult<Self> {
466 match self {
467 Column::Series(s) => s.cast_with_options(dtype, options).map(Column::from),
468 Column::Scalar(s) => s.cast_with_options(dtype, options).map(Column::from),
469 }
470 }
471 pub fn strict_cast(&self, dtype: &DataType) -> PolarsResult<Self> {
472 match self {
473 Column::Series(s) => s.strict_cast(dtype).map(Column::from),
474 Column::Scalar(s) => s.strict_cast(dtype).map(Column::from),
475 }
476 }
477 pub fn cast(&self, dtype: &DataType) -> PolarsResult<Column> {
478 match self {
479 Column::Series(s) => s.cast(dtype).map(Column::from),
480 Column::Scalar(s) => s.cast(dtype).map(Column::from),
481 }
482 }
483 pub unsafe fn cast_unchecked(&self, dtype: &DataType) -> PolarsResult<Column> {
487 match self {
488 Column::Series(s) => unsafe { s.cast_unchecked(dtype) }.map(Column::from),
489 Column::Scalar(s) => unsafe { s.cast_unchecked(dtype) }.map(Column::from),
490 }
491 }
492
493 #[must_use]
494 pub fn clear(&self) -> Self {
495 match self {
496 Column::Series(s) => s.clear().into(),
497 Column::Scalar(s) => s.resize(0).into(),
498 }
499 }
500
501 #[inline]
502 pub fn shrink_to_fit(&mut self) {
503 match self {
504 Column::Series(s) => s.shrink_to_fit(),
505 Column::Scalar(_) => {},
506 }
507 }
508
509 #[inline]
510 pub fn new_from_index(&self, index: usize, length: usize) -> Self {
511 if index >= self.len() {
512 return Self::full_null(self.name().clone(), length, self.dtype());
513 }
514
515 match self {
516 Column::Series(s) => {
517 let av = unsafe { s.get_unchecked(index) };
519 let scalar = Scalar::new(self.dtype().clone(), av.into_static());
520 Self::new_scalar(self.name().clone(), scalar, length)
521 },
522 Column::Scalar(s) => s.resize(length).into(),
523 }
524 }
525
526 pub fn broadcast_to(&self, length: usize) -> PolarsResult<Cow<'_, Self>> {
530 let len = self.len();
531 if len == length {
532 Ok(Cow::Borrowed(self))
533 } else if len == 1 {
534 Ok(Cow::Owned(self.new_from_index(0, length)))
535 } else {
536 polars_bail!(
537 ShapeMismatch: "can't broadcast Series '{}' of length {len} to length {length}",
538 self.name()
539 );
540 }
541 }
542
543 pub fn broadcast_in_place_to(&mut self, length: usize) -> PolarsResult<()> {
545 if let Cow::Owned(new) = self.broadcast_to(length)? {
546 *self = new;
547 }
548 Ok(())
549 }
550
551 pub fn broadcast_owned_to(mut self, length: usize) -> PolarsResult<Self> {
553 self.broadcast_in_place_to(length)?;
554 Ok(self)
555 }
556
557 #[inline]
558 pub fn has_nulls(&self) -> bool {
559 match self {
560 Self::Series(s) => s.has_nulls(),
561 Self::Scalar(s) => s.has_nulls(),
562 }
563 }
564
565 #[inline]
566 pub fn is_null(&self) -> BooleanChunked {
567 match self {
568 Self::Series(s) => s.is_null(),
569 Self::Scalar(s) => {
570 BooleanChunked::full(s.name().clone(), s.scalar().is_null(), s.len())
571 },
572 }
573 }
574 #[inline]
575 pub fn is_not_null(&self) -> BooleanChunked {
576 match self {
577 Self::Series(s) => s.is_not_null(),
578 Self::Scalar(s) => {
579 BooleanChunked::full(s.name().clone(), !s.scalar().is_null(), s.len())
580 },
581 }
582 }
583
584 pub fn to_physical_repr(&self) -> Column {
585 self.as_materialized_series()
587 .to_physical_repr()
588 .into_owned()
589 .into()
590 }
591 pub unsafe fn from_physical_unchecked(&self, dtype: &DataType) -> PolarsResult<Column> {
595 self.as_materialized_series()
597 .from_physical_unchecked(dtype)
598 .map(Column::from)
599 }
600
601 pub fn head(&self, length: Option<usize>) -> Column {
602 let len = length.unwrap_or(HEAD_DEFAULT_LENGTH);
603 let len = usize::min(len, self.len());
604 self.slice(0, len)
605 }
606 pub fn tail(&self, length: Option<usize>) -> Column {
607 let len = length.unwrap_or(TAIL_DEFAULT_LENGTH);
608 let len = usize::min(len, self.len());
609 debug_assert!(len <= i64::MAX as usize);
610 self.slice(-(len as i64), len)
611 }
612 pub fn slice(&self, offset: i64, length: usize) -> Column {
613 match self {
614 Column::Series(s) => s.slice(offset, length).into(),
615 Column::Scalar(s) => {
616 let (_, length) = slice_offsets(offset, length, s.len());
617 s.resize(length).into()
618 },
619 }
620 }
621
622 pub fn split_at(&self, offset: i64) -> (Column, Column) {
623 match self {
624 Column::Scalar(c) => {
625 let len = c.len();
626 let offset = if offset < 0 {
627 let offset_abs = usize::try_from(offset.strict_abs())
628 .expect("offset exceeds usize limits")
629 .min(len);
630 len - offset_abs
631 } else {
632 usize::try_from(offset)
633 .expect("offset exceeds usize limits")
634 .min(len)
635 };
636 (
637 Column::Scalar(c.resize(offset)),
638 Column::Scalar(c.resize(len - offset)),
639 )
640 },
641 Column::Series(_) => {
642 let (l, r) = self.as_materialized_series().split_at(offset);
643 (l.into(), r.into())
644 },
645 }
646 }
647
648 #[inline]
649 pub fn null_count(&self) -> usize {
650 match self {
651 Self::Series(s) => s.null_count(),
652 Self::Scalar(s) if s.scalar().is_null() => s.len(),
653 Self::Scalar(_) => 0,
654 }
655 }
656
657 pub fn first_non_null(&self) -> Option<usize> {
658 match self {
659 Self::Series(s) => crate::utils::first_non_null(s.chunks().iter().map(|a| a.as_ref())),
660 Self::Scalar(s) => (!s.scalar().is_null() && !s.is_empty()).then_some(0),
661 }
662 }
663
664 pub fn last_non_null(&self) -> Option<usize> {
665 match self {
666 Self::Series(s) => {
667 crate::utils::last_non_null(s.chunks().iter().map(|a| a.as_ref()), s.len())
668 },
669 Self::Scalar(s) => (!s.scalar().is_null() && !s.is_empty()).then(|| s.len() - 1),
670 }
671 }
672
673 pub fn take(&self, indices: &IdxCa) -> PolarsResult<Column> {
674 check_bounds_ca(indices, self.len() as IdxSize)?;
675 Ok(unsafe { self.take_unchecked(indices) })
676 }
677 pub fn take_slice(&self, indices: &[IdxSize]) -> PolarsResult<Column> {
678 check_bounds(indices, self.len() as IdxSize)?;
679 Ok(unsafe { self.take_slice_unchecked(indices) })
680 }
681 pub unsafe fn take_unchecked(&self, indices: &IdxCa) -> Column {
685 debug_assert!(check_bounds_ca(indices, self.len() as IdxSize).is_ok());
686
687 match self {
688 Self::Series(s) => unsafe { s.take_unchecked(indices) }.into(),
689 Self::Scalar(s) => {
690 let idxs_length = indices.len();
691 let idxs_null_count = indices.null_count();
692
693 let scalar = ScalarColumn::from_single_value_series(
694 s.as_single_value_series().take_unchecked(&IdxCa::new(
695 indices.name().clone(),
696 &[0][..s.len().min(1)],
697 )),
698 idxs_length,
699 );
700
701 if idxs_null_count == 0 || scalar.has_nulls() {
703 scalar.into_column()
704 } else if idxs_null_count == idxs_length {
705 scalar.into_nulls().into_column()
706 } else {
707 let validity = indices.rechunk_validity();
708 let series = scalar.take_materialized_series();
709 let name = series.name().clone();
710 let dtype = series.dtype().clone();
711 let mut chunks = series.into_chunks();
712 assert_eq!(chunks.len(), 1);
713 chunks[0] = chunks[0].with_validity(validity);
714 unsafe { Series::from_chunks_and_dtype_unchecked(name, chunks, &dtype) }
715 .into_column()
716 }
717 },
718 }
719 }
720 pub unsafe fn take_slice_unchecked(&self, indices: &[IdxSize]) -> Column {
724 debug_assert!(check_bounds(indices, self.len() as IdxSize).is_ok());
725
726 match self {
727 Self::Series(s) => unsafe { s.take_slice_unchecked(indices) }.into(),
728 Self::Scalar(s) => ScalarColumn::from_single_value_series(
729 s.as_single_value_series()
730 .take_slice_unchecked(&[0][..s.len().min(1)]),
731 indices.len(),
732 )
733 .into(),
734 }
735 }
736
737 #[inline(always)]
739 #[cfg(any(feature = "algorithm_group_by", feature = "bitwise"))]
740 fn agg_with_scalar_identity(
741 &self,
742 groups: &GroupsType,
743 series_agg: impl Fn(&Series, &GroupsType) -> Series,
744 ) -> Column {
745 match self {
746 Column::Series(s) => series_agg(s, groups).into_column(),
747 Column::Scalar(s) => {
748 if s.is_empty() {
749 return series_agg(s.as_materialized_series(), groups).into_column();
750 }
751
752 let series_aggregation = series_agg(
756 &s.as_single_value_series(),
757 &GroupsType::new_slice(vec![[0, 1]], false, true),
759 );
760
761 if series_aggregation.has_nulls() {
763 return Self::new_scalar(
764 series_aggregation.name().clone(),
765 Scalar::new(series_aggregation.dtype().clone(), AnyValue::Null),
766 groups.len(),
767 );
768 }
769
770 let mut scalar_col = s.resize(groups.len());
771 if series_aggregation.dtype() != s.dtype() {
774 scalar_col = scalar_col.cast(series_aggregation.dtype()).unwrap();
775 }
776
777 let Some(first_empty_idx) = groups.iter().position(|g| g.is_empty()) else {
778 return scalar_col.into_column();
780 };
781
782 let mut validity = BitmapBuilder::with_capacity(groups.len());
784 validity.extend_constant(first_empty_idx, true);
785 let iter = unsafe {
787 TrustMyLength::new(
788 groups.iter().skip(first_empty_idx).map(|g| !g.is_empty()),
789 groups.len() - first_empty_idx,
790 )
791 };
792 validity.extend_trusted_len_iter(iter);
793
794 let mut s = scalar_col.take_materialized_series().rechunk();
795 let chunks = unsafe { s.chunks_mut() };
797 let arr = &mut chunks[0];
798 *arr = arr.with_validity(validity.into_opt_validity());
799 s.compute_len();
800
801 s.into_column()
802 },
803 }
804 }
805
806 #[cfg(feature = "algorithm_group_by")]
810 pub unsafe fn agg_min(&self, groups: &GroupsType) -> Self {
811 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_min(g) })
812 }
813
814 #[cfg(feature = "algorithm_group_by")]
818 pub unsafe fn agg_max(&self, groups: &GroupsType) -> Self {
819 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_max(g) })
820 }
821
822 #[cfg(feature = "algorithm_group_by")]
826 pub unsafe fn agg_mean(&self, groups: &GroupsType) -> Self {
827 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_mean(g) })
828 }
829
830 #[cfg(feature = "algorithm_group_by")]
834 pub unsafe fn agg_arg_min(&self, groups: &GroupsType) -> Self {
835 match self {
836 Column::Series(s) => unsafe { Column::from(s.agg_arg_min(groups)) },
837 Column::Scalar(sc) => {
838 let scalar = if sc.is_empty() || sc.has_nulls() {
839 Scalar::null(IDX_DTYPE)
840 } else {
841 Scalar::new_idxsize(0)
842 };
843 Column::new_scalar(self.name().clone(), scalar, 1)
844 },
845 }
846 }
847
848 #[cfg(feature = "algorithm_group_by")]
852 pub unsafe fn agg_arg_max(&self, groups: &GroupsType) -> Self {
853 match self {
854 Column::Series(s) => unsafe { Column::from(s.agg_arg_max(groups)) },
855 Column::Scalar(sc) => {
856 let scalar = if sc.is_empty() || sc.has_nulls() {
857 Scalar::null(IDX_DTYPE)
858 } else {
859 Scalar::new_idxsize(0)
860 };
861 Column::new_scalar(self.name().clone(), scalar, 1)
862 },
863 }
864 }
865
866 #[cfg(feature = "algorithm_group_by")]
870 pub unsafe fn agg_sum(&self, groups: &GroupsType) -> Self {
871 unsafe { self.as_materialized_series().agg_sum(groups) }.into()
873 }
874
875 #[cfg(feature = "algorithm_group_by")]
879 pub unsafe fn agg_first(&self, groups: &GroupsType) -> Self {
880 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_first(g) })
881 }
882
883 #[cfg(feature = "algorithm_group_by")]
887 pub unsafe fn agg_first_non_null(&self, groups: &GroupsType) -> Self {
888 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_first_non_null(g) })
889 }
890
891 #[cfg(feature = "algorithm_group_by")]
895 pub unsafe fn agg_last(&self, groups: &GroupsType) -> Self {
896 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_last(g) })
897 }
898
899 #[cfg(feature = "algorithm_group_by")]
903 pub unsafe fn agg_last_non_null(&self, groups: &GroupsType) -> Self {
904 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_last_non_null(g) })
905 }
906
907 #[cfg(feature = "algorithm_group_by")]
911 pub unsafe fn agg_n_unique(&self, groups: &GroupsType) -> Self {
912 unsafe { self.as_materialized_series().agg_n_unique(groups) }.into()
914 }
915
916 #[cfg(feature = "algorithm_group_by")]
920 pub unsafe fn agg_quantile(
921 &self,
922 groups: &GroupsType,
923 quantile: f64,
924 method: QuantileMethod,
925 ) -> Self {
926 unsafe {
929 self.as_materialized_series()
930 .agg_quantile(groups, quantile, method)
931 }
932 .into()
933 }
934
935 #[cfg(feature = "algorithm_group_by")]
939 pub unsafe fn agg_median(&self, groups: &GroupsType) -> Self {
940 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_median(g) })
941 }
942
943 #[cfg(feature = "algorithm_group_by")]
947 pub unsafe fn agg_var(&self, groups: &GroupsType, ddof: u8) -> Self {
948 unsafe { self.as_materialized_series().agg_var(groups, ddof) }.into()
950 }
951
952 #[cfg(feature = "algorithm_group_by")]
956 pub unsafe fn agg_std(&self, groups: &GroupsType, ddof: u8) -> Self {
957 unsafe { self.as_materialized_series().agg_std(groups, ddof) }.into()
959 }
960
961 #[cfg(feature = "algorithm_group_by")]
965 pub unsafe fn agg_list(&self, groups: &GroupsType) -> Self {
966 unsafe { self.as_materialized_series().agg_list(groups) }.into()
968 }
969
970 #[cfg(feature = "algorithm_group_by")]
974 pub fn agg_valid_count(&self, groups: &GroupsType) -> Self {
975 unsafe { self.as_materialized_series().agg_valid_count(groups) }.into()
977 }
978
979 #[cfg(feature = "bitwise")]
983 pub unsafe fn agg_and(&self, groups: &GroupsType) -> Self {
984 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_and(g) })
985 }
986 #[cfg(feature = "bitwise")]
990 pub unsafe fn agg_or(&self, groups: &GroupsType) -> Self {
991 self.agg_with_scalar_identity(groups, |s, g| unsafe { s.agg_or(g) })
992 }
993 #[cfg(feature = "bitwise")]
997 pub unsafe fn agg_xor(&self, groups: &GroupsType) -> Self {
998 unsafe { self.as_materialized_series().agg_xor(groups) }.into()
1000 }
1001
1002 pub fn full_null(name: PlSmallStr, size: usize, dtype: &DataType) -> Self {
1003 Self::new_scalar(name, Scalar::new(dtype.clone(), AnyValue::Null), size)
1004 }
1005
1006 pub fn is_empty(&self) -> bool {
1007 self.len() == 0
1008 }
1009
1010 pub fn is_full_null(&self) -> bool {
1011 match self {
1012 Column::Series(s) => s.is_full_null(),
1013 Column::Scalar(s) => s.is_full_null(),
1014 }
1015 }
1016
1017 pub fn reverse(&self) -> Column {
1018 match self {
1019 Column::Series(s) => s.reverse().into(),
1020 Column::Scalar(_) => self.clone(),
1021 }
1022 }
1023
1024 pub fn equals(&self, other: &Column) -> bool {
1025 self.as_materialized_series()
1027 .equals(other.as_materialized_series())
1028 }
1029
1030 pub fn equals_missing(&self, other: &Column) -> bool {
1031 self.as_materialized_series()
1033 .equals_missing(other.as_materialized_series())
1034 }
1035
1036 pub fn set_sorted_flag(&mut self, sorted: IsSorted) {
1037 match self {
1039 Column::Series(s) => s.set_sorted_flag(sorted),
1040 Column::Scalar(_) => {},
1041 }
1042 }
1043
1044 pub fn get_flags(&self) -> StatisticsFlags {
1045 match self {
1046 Column::Series(s) => s.get_flags(),
1047 Column::Scalar(_) => {
1048 StatisticsFlags::IS_SORTED_ASC | StatisticsFlags::CAN_FAST_EXPLODE_LIST
1049 },
1050 }
1051 }
1052
1053 pub fn set_flags(&mut self, flags: StatisticsFlags) -> bool {
1055 match self {
1056 Column::Series(s) => {
1057 s.set_flags(flags);
1058 true
1059 },
1060 Column::Scalar(_) => false,
1061 }
1062 }
1063
1064 pub fn vec_hash(
1065 &self,
1066 build_hasher: PlSeedableRandomStateQuality,
1067 buf: &mut Vec<u64>,
1068 ) -> PolarsResult<()> {
1069 self.as_materialized_series().vec_hash(build_hasher, buf)
1071 }
1072
1073 pub fn vec_hash_combine(
1074 &self,
1075 build_hasher: PlSeedableRandomStateQuality,
1076 hashes: &mut [u64],
1077 ) -> PolarsResult<()> {
1078 self.as_materialized_series()
1080 .vec_hash_combine(build_hasher, hashes)
1081 }
1082
1083 pub fn append(&mut self, other: &Column) -> PolarsResult<&mut Self> {
1084 self.into_materialized_series()
1086 .append(other.as_materialized_series())?;
1087 Ok(self)
1088 }
1089 pub fn append_owned(&mut self, other: Column) -> PolarsResult<&mut Self> {
1090 self.into_materialized_series()
1091 .append_owned(other.take_materialized_series())?;
1092 Ok(self)
1093 }
1094
1095 pub fn arg_sort(&self, options: SortOptions) -> IdxCa {
1096 if self.is_empty() {
1097 return IdxCa::from_vec(self.name().clone(), Vec::new());
1098 }
1099
1100 if self.null_count() == self.len() {
1101 return IdxCa::from_iter_values(self.name().clone(), 0..self.len() as IdxSize);
1104 }
1105
1106 let is_sorted = Some(self.is_sorted_flag());
1107 let Some(is_sorted) = is_sorted.filter(|v| !matches!(v, IsSorted::Not)) else {
1108 return self.as_materialized_series().arg_sort(options);
1109 };
1110
1111 let is_sorted_dsc = matches!(is_sorted, IsSorted::Descending);
1113 let invert = options.descending != is_sorted_dsc;
1114
1115 let mut values = Vec::with_capacity(self.len());
1116
1117 #[inline(never)]
1118 fn extend(
1119 start: IdxSize,
1120 end: IdxSize,
1121 slf: &Column,
1122 values: &mut Vec<IdxSize>,
1123 is_only_nulls: bool,
1124 invert: bool,
1125 maintain_order: bool,
1126 ) {
1127 debug_assert!(start <= end);
1128 debug_assert!(start as usize <= slf.len());
1129 debug_assert!(end as usize <= slf.len());
1130
1131 if !invert || is_only_nulls {
1132 values.extend(start..end);
1133 return;
1134 }
1135
1136 if !maintain_order {
1138 values.extend((start..end).rev());
1139 return;
1140 }
1141
1142 let arg_unique = slf
1148 .slice(start as i64, (end - start) as usize)
1149 .arg_unique()
1150 .unwrap();
1151
1152 assert!(!arg_unique.has_nulls());
1153
1154 let num_unique = arg_unique.len();
1155
1156 if num_unique == (end - start) as usize {
1158 values.extend((start..end).rev());
1159 return;
1160 }
1161
1162 if num_unique == 1 {
1163 values.extend(start..end);
1164 return;
1165 }
1166
1167 let mut prev_idx = end - start;
1168 for chunk in arg_unique.downcast_iter() {
1169 for &idx in chunk.values().as_slice().iter().rev() {
1170 values.extend(start + idx..start + prev_idx);
1171 prev_idx = idx;
1172 }
1173 }
1174 }
1175 macro_rules! extend {
1176 ($start:expr, $end:expr) => {
1177 extend!($start, $end, is_only_nulls = false);
1178 };
1179 ($start:expr, $end:expr, is_only_nulls = $is_only_nulls:expr) => {
1180 extend(
1181 $start,
1182 $end,
1183 self,
1184 &mut values,
1185 $is_only_nulls,
1186 invert,
1187 options.maintain_order,
1188 );
1189 };
1190 }
1191
1192 let length = self.len() as IdxSize;
1193 let null_count = self.null_count() as IdxSize;
1194
1195 if null_count == 0 {
1196 extend!(0, length);
1197 } else {
1198 let has_nulls_last = self.get(self.len() - 1).unwrap().is_null();
1199 match (options.nulls_last, has_nulls_last) {
1200 (true, true) => {
1201 extend!(0, length - null_count);
1203 extend!(length - null_count, length, is_only_nulls = true);
1204 },
1205 (true, false) => {
1206 extend!(null_count, length);
1208 extend!(0, null_count, is_only_nulls = true);
1209 },
1210 (false, true) => {
1211 extend!(length - null_count, length, is_only_nulls = true);
1213 extend!(0, length - null_count);
1214 },
1215 (false, false) => {
1216 extend!(0, null_count, is_only_nulls = true);
1218 extend!(null_count, length);
1219 },
1220 }
1221 }
1222
1223 if let Some(limit) = options.limit {
1226 let limit = limit.min(length);
1227 values.truncate(limit as usize);
1228 }
1229
1230 IdxCa::from_vec(self.name().clone(), values)
1231 }
1232
1233 pub fn arg_sort_multiple(
1234 &self,
1235 by: &[Column],
1236 options: &SortMultipleOptions,
1237 ) -> PolarsResult<IdxCa> {
1238 self.as_materialized_series().arg_sort_multiple(by, options)
1240 }
1241
1242 pub fn arg_unique(&self) -> PolarsResult<IdxCa> {
1243 match self {
1244 Column::Scalar(s) => Ok(IdxCa::new_vec(s.name().clone(), vec![0])),
1245 _ => self.as_materialized_series().arg_unique(),
1246 }
1247 }
1248
1249 pub fn bit_repr(&self) -> Option<BitRepr> {
1250 self.as_materialized_series().bit_repr()
1252 }
1253
1254 pub fn into_frame(self) -> DataFrame {
1255 unsafe { DataFrame::new_unchecked(self.len(), vec![self]) }
1257 }
1258
1259 pub fn extend(&mut self, other: &Column) -> PolarsResult<&mut Self> {
1260 self.into_materialized_series()
1262 .extend(other.as_materialized_series())?;
1263 Ok(self)
1264 }
1265
1266 pub fn rechunk(&self) -> Column {
1267 match self {
1268 Column::Series(s) => s.rechunk().into(),
1269 Column::Scalar(s) => {
1270 if s.lazy_as_materialized_series()
1271 .filter(|x| x.n_chunks() > 1)
1272 .is_some()
1273 {
1274 Column::Scalar(ScalarColumn::new(
1275 s.name().clone(),
1276 s.scalar().clone(),
1277 s.len(),
1278 ))
1279 } else {
1280 self.clone()
1281 }
1282 },
1283 }
1284 }
1285
1286 pub fn explode(&self, options: ExplodeOptions) -> PolarsResult<Column> {
1287 self.as_materialized_series()
1288 .explode(options)
1289 .map(Column::from)
1290 }
1291 pub fn implode(&self) -> PolarsResult<ListChunked> {
1292 self.as_materialized_series().implode()
1293 }
1294
1295 pub fn fill_null(&self, strategy: FillNullStrategy) -> PolarsResult<Self> {
1296 self.as_materialized_series()
1298 .fill_null(strategy)
1299 .map(Column::from)
1300 }
1301
1302 pub fn divide(&self, rhs: &Column) -> PolarsResult<Self> {
1303 self.as_materialized_series()
1305 .divide(rhs.as_materialized_series())
1306 .map(Column::from)
1307 }
1308
1309 pub fn shift(&self, periods: i64) -> Column {
1310 self.as_materialized_series().shift(periods).into()
1312 }
1313
1314 pub fn with_validity(&self, validity: Option<Bitmap>) -> Column {
1315 match self {
1316 Column::Series(s) => Column::from(s.with_validity(validity)),
1317 Column::Scalar(s) => match validity {
1318 Some(v) => Column::from(s.as_materialized_series().with_validity(Some(v))),
1319 None => Column::Scalar(s.clone()),
1320 },
1321 }
1322 }
1323
1324 pub fn mask(&self, validity: &Bitmap) -> Column {
1325 if validity.len() == 1 {
1326 if validity.get_bit(0) {
1327 self.clone()
1328 } else {
1329 Self::full_null(self.name().clone(), self.len(), self.dtype())
1330 }
1331 } else {
1332 Column::from(self.as_materialized_series().mask(validity))
1333 }
1334 }
1335
1336 #[cfg(feature = "zip_with")]
1337 pub fn zip_with(&self, mask: &BooleanChunked, other: &Self) -> PolarsResult<Self> {
1338 self.as_materialized_series()
1340 .zip_with(mask, other.as_materialized_series())
1341 .map(Self::from)
1342 }
1343
1344 #[cfg(feature = "zip_with")]
1345 pub fn zip_with_same_type(
1346 &self,
1347 mask: &ChunkedArray<BooleanType>,
1348 other: &Column,
1349 ) -> PolarsResult<Column> {
1350 self.as_materialized_series()
1352 .zip_with_same_type(mask, other.as_materialized_series())
1353 .map(Column::from)
1354 }
1355
1356 pub fn drop_nulls(&self) -> Column {
1357 match self {
1358 Column::Series(s) => s.drop_nulls().into_column(),
1359 Column::Scalar(s) => s.drop_nulls().into_column(),
1360 }
1361 }
1362
1363 pub fn to_unit_list(&self) -> Column {
1365 match self {
1367 Column::Series(s) => s.to_unit_list().into_column(),
1368 Column::Scalar(s) => s.to_unit_list().into_column(),
1369 }
1370 }
1371
1372 pub fn is_sorted_flag(&self) -> IsSorted {
1373 match self {
1374 Column::Series(s) => s.is_sorted_flag(),
1375 Column::Scalar(_) => IsSorted::Ascending,
1376 }
1377 }
1378
1379 pub fn unique(&self) -> PolarsResult<Column> {
1380 match self {
1381 Column::Series(s) => s.unique().map(Column::from),
1382 Column::Scalar(s) => {
1383 _ = s.as_single_value_series().unique()?;
1384 if s.is_empty() {
1385 return Ok(s.clone().into_column());
1386 }
1387
1388 Ok(s.resize(1).into_column())
1389 },
1390 }
1391 }
1392 pub fn unique_stable(&self) -> PolarsResult<Column> {
1393 match self {
1394 Column::Series(s) => s.unique_stable().map(Column::from),
1395 Column::Scalar(s) => {
1396 _ = s.as_single_value_series().unique_stable()?;
1397 if s.is_empty() {
1398 return Ok(s.clone().into_column());
1399 }
1400
1401 Ok(s.resize(1).into_column())
1402 },
1403 }
1404 }
1405
1406 pub fn reshape_list(&self, dimensions: &[ReshapeDimension]) -> PolarsResult<Self> {
1407 self.as_materialized_series()
1409 .reshape_list(dimensions)
1410 .map(Self::from)
1411 }
1412
1413 #[cfg(feature = "dtype-array")]
1414 pub fn reshape_array(&self, dimensions: &[ReshapeDimension]) -> PolarsResult<Self> {
1415 self.as_materialized_series()
1417 .reshape_array(dimensions)
1418 .map(Self::from)
1419 }
1420
1421 pub fn sort(&self, sort_options: SortOptions) -> PolarsResult<Self> {
1422 self.as_materialized_series()
1424 .sort(sort_options)
1425 .map(Self::from)
1426 }
1427
1428 pub fn filter(&self, filter: &BooleanChunked) -> PolarsResult<Self> {
1429 match self {
1430 Column::Series(s) => s.filter(filter).map(Column::from),
1431 Column::Scalar(s) => {
1432 if s.is_empty() {
1433 return Ok(s.clone().into_column());
1434 }
1435
1436 if filter.len() == 1 {
1438 return match filter.get(0) {
1439 Some(true) => Ok(s.clone().into_column()),
1440 _ => Ok(s.resize(0).into_column()),
1441 };
1442 }
1443
1444 Ok(s.resize(filter.sum().unwrap() as usize).into_column())
1445 },
1446 }
1447 }
1448
1449 #[cfg(feature = "random")]
1450 pub fn shuffle(&self, seed: Option<u64>) -> Self {
1451 self.as_materialized_series().shuffle(seed).into()
1453 }
1454
1455 #[cfg(feature = "random")]
1456 pub fn sample_frac(
1457 &self,
1458 frac: f64,
1459 with_replacement: bool,
1460 shuffle: Option<bool>,
1461 seed: Option<u64>,
1462 ) -> PolarsResult<Self> {
1463 self.as_materialized_series()
1464 .sample_frac(frac, with_replacement, shuffle, seed)
1465 .map(Self::from)
1466 }
1467
1468 #[cfg(feature = "random")]
1469 pub fn sample_n(
1470 &self,
1471 n: usize,
1472 with_replacement: bool,
1473 shuffle: Option<bool>,
1474 seed: Option<u64>,
1475 ) -> PolarsResult<Self> {
1476 self.as_materialized_series()
1477 .sample_n(n, with_replacement, shuffle, seed)
1478 .map(Self::from)
1479 }
1480
1481 pub fn gather_every(&self, n: usize, offset: usize) -> PolarsResult<Column> {
1482 polars_ensure!(n > 0, InvalidOperation: "gather_every(n): n should be positive");
1483 if self.len().saturating_sub(offset) == 0 {
1484 return Ok(self.clear());
1485 }
1486
1487 match self {
1488 Column::Series(s) => Ok(s.gather_every(n, offset)?.into()),
1489 Column::Scalar(s) => {
1490 let total = s.len() - offset;
1491 Ok(s.resize(1 + (total - 1) / n).into())
1492 },
1493 }
1494 }
1495
1496 pub fn extend_constant(&self, value: AnyValue, n: usize) -> PolarsResult<Self> {
1497 if self.is_empty() {
1498 return Ok(Self::new_scalar(
1499 self.name().clone(),
1500 Scalar::new(self.dtype().clone(), value.into_static()),
1501 n,
1502 ));
1503 }
1504
1505 match self {
1506 Column::Series(s) => s.extend_constant(value, n).map(Column::from),
1507 Column::Scalar(s) => {
1508 if s.scalar().as_any_value() == value {
1509 Ok(s.resize(s.len() + n).into())
1510 } else {
1511 s.as_materialized_series()
1512 .extend_constant(value, n)
1513 .map(Column::from)
1514 }
1515 },
1516 }
1517 }
1518
1519 pub fn is_finite(&self) -> PolarsResult<BooleanChunked> {
1520 self.try_map_unary_elementwise_to_bool(|s| s.is_finite())
1521 }
1522 pub fn is_infinite(&self) -> PolarsResult<BooleanChunked> {
1523 self.try_map_unary_elementwise_to_bool(|s| s.is_infinite())
1524 }
1525 pub fn is_nan(&self) -> PolarsResult<BooleanChunked> {
1526 self.try_map_unary_elementwise_to_bool(|s| s.is_nan())
1527 }
1528 pub fn is_not_nan(&self) -> PolarsResult<BooleanChunked> {
1529 self.try_map_unary_elementwise_to_bool(|s| s.is_not_nan())
1530 }
1531
1532 pub fn wrapping_trunc_div_scalar<T>(&self, rhs: T) -> Self
1533 where
1534 T: Num + NumCast,
1535 {
1536 self.as_materialized_series()
1538 .wrapping_trunc_div_scalar(rhs)
1539 .into()
1540 }
1541
1542 pub fn product(&self) -> PolarsResult<Scalar> {
1543 self.as_materialized_series().product()
1545 }
1546
1547 pub fn phys_iter(&self) -> SeriesPhysIter<'_> {
1548 self.as_materialized_series().phys_iter()
1550 }
1551
1552 #[inline]
1553 pub fn get(&self, index: usize) -> PolarsResult<AnyValue<'_>> {
1554 polars_ensure!(index < self.len(), oob = index, self.len());
1555
1556 Ok(unsafe { self.get_unchecked(index) })
1558 }
1559 #[inline(always)]
1563 pub unsafe fn get_unchecked(&self, index: usize) -> AnyValue<'_> {
1564 debug_assert!(index < self.len());
1565
1566 match self {
1567 Column::Series(s) => unsafe { s.get_unchecked(index) },
1568 Column::Scalar(s) => s.scalar().as_any_value(),
1569 }
1570 }
1571
1572 #[cfg(feature = "object")]
1573 pub fn get_object(
1574 &self,
1575 index: usize,
1576 ) -> Option<&dyn crate::chunked_array::object::PolarsObjectSafe> {
1577 self.as_materialized_series().get_object(index)
1578 }
1579
1580 pub fn bitand(&self, rhs: &Self) -> PolarsResult<Self> {
1581 self.try_apply_broadcasting_binary_elementwise(rhs, |l, r| l & r)
1582 }
1583 pub fn bitor(&self, rhs: &Self) -> PolarsResult<Self> {
1584 self.try_apply_broadcasting_binary_elementwise(rhs, |l, r| l | r)
1585 }
1586 pub fn bitxor(&self, rhs: &Self) -> PolarsResult<Self> {
1587 self.try_apply_broadcasting_binary_elementwise(rhs, |l, r| l ^ r)
1588 }
1589
1590 pub fn try_add_owned(self, other: Self) -> PolarsResult<Self> {
1591 match (self, other) {
1592 (Column::Series(lhs), Column::Series(rhs)) => {
1593 lhs.take().try_add_owned(rhs.take()).map(Column::from)
1594 },
1595 (lhs, rhs) => lhs + rhs,
1596 }
1597 }
1598 pub fn try_sub_owned(self, other: Self) -> PolarsResult<Self> {
1599 match (self, other) {
1600 (Column::Series(lhs), Column::Series(rhs)) => {
1601 lhs.take().try_sub_owned(rhs.take()).map(Column::from)
1602 },
1603 (lhs, rhs) => lhs - rhs,
1604 }
1605 }
1606 pub fn try_mul_owned(self, other: Self) -> PolarsResult<Self> {
1607 match (self, other) {
1608 (Column::Series(lhs), Column::Series(rhs)) => {
1609 lhs.take().try_mul_owned(rhs.take()).map(Column::from)
1610 },
1611 (lhs, rhs) => lhs * rhs,
1612 }
1613 }
1614
1615 pub(crate) fn str_value(&self, index: usize) -> PolarsResult<Cow<'_, str>> {
1616 Ok(self.get(index)?.str_value())
1617 }
1618
1619 pub fn min_reduce(&self) -> PolarsResult<Scalar> {
1620 match self {
1621 Column::Series(s) => s.min_reduce(),
1622 Column::Scalar(s) => {
1623 s.as_single_value_series().min_reduce()
1626 },
1627 }
1628 }
1629 pub fn max_reduce(&self) -> PolarsResult<Scalar> {
1630 match self {
1631 Column::Series(s) => s.max_reduce(),
1632 Column::Scalar(s) => {
1633 s.as_single_value_series().max_reduce()
1636 },
1637 }
1638 }
1639 pub fn median_reduce(&self) -> PolarsResult<Scalar> {
1640 match self {
1641 Column::Series(s) => s.median_reduce(),
1642 Column::Scalar(s) => {
1643 s.as_single_value_series().median_reduce()
1646 },
1647 }
1648 }
1649 pub fn mean_reduce(&self) -> PolarsResult<Scalar> {
1650 match self {
1651 Column::Series(s) => s.mean_reduce(),
1652 Column::Scalar(s) => {
1653 s.as_single_value_series().mean_reduce()
1656 },
1657 }
1658 }
1659 pub fn std_reduce(&self, ddof: u8) -> PolarsResult<Scalar> {
1660 match self {
1661 Column::Series(s) => s.std_reduce(ddof),
1662 Column::Scalar(s) => {
1663 let n = s.len().min(ddof as usize + 1);
1666 s.as_n_values_series(n).std_reduce(ddof)
1667 },
1668 }
1669 }
1670 pub fn var_reduce(&self, ddof: u8) -> PolarsResult<Scalar> {
1671 match self {
1672 Column::Series(s) => s.var_reduce(ddof),
1673 Column::Scalar(s) => {
1674 let n = s.len().min(ddof as usize + 1);
1677 s.as_n_values_series(n).var_reduce(ddof)
1678 },
1679 }
1680 }
1681 pub fn sum_reduce(&self) -> PolarsResult<Scalar> {
1682 self.as_materialized_series().sum_reduce()
1684 }
1685 pub fn and_reduce(&self) -> PolarsResult<Scalar> {
1686 match self {
1687 Column::Series(s) => s.and_reduce(),
1688 Column::Scalar(s) => {
1689 s.as_single_value_series().and_reduce()
1692 },
1693 }
1694 }
1695 pub fn or_reduce(&self) -> PolarsResult<Scalar> {
1696 match self {
1697 Column::Series(s) => s.or_reduce(),
1698 Column::Scalar(s) => {
1699 s.as_single_value_series().or_reduce()
1702 },
1703 }
1704 }
1705 pub fn xor_reduce(&self) -> PolarsResult<Scalar> {
1706 match self {
1707 Column::Series(s) => s.xor_reduce(),
1708 Column::Scalar(s) => {
1709 s.as_n_values_series(2 - s.len() % 2).xor_reduce()
1716 },
1717 }
1718 }
1719 pub fn n_unique(&self) -> PolarsResult<usize> {
1720 match self {
1721 Column::Series(s) => s.n_unique(),
1722 Column::Scalar(s) => s.as_single_value_series().n_unique(),
1723 }
1724 }
1725
1726 pub fn quantile_reduce(&self, quantile: f64, method: QuantileMethod) -> PolarsResult<Scalar> {
1727 self.as_materialized_series()
1728 .quantile_reduce(quantile, method)
1729 }
1730
1731 pub fn quantiles_reduce(
1732 &self,
1733 quantiles: &[f64],
1734 method: QuantileMethod,
1735 ) -> PolarsResult<Scalar> {
1736 self.as_materialized_series()
1737 .quantiles_reduce(quantiles, method)
1738 }
1739
1740 pub(crate) fn estimated_size(&self) -> usize {
1741 self.as_materialized_series().estimated_size()
1743 }
1744
1745 pub fn sort_with(&self, options: SortOptions) -> PolarsResult<Self> {
1746 match self {
1747 Column::Series(s) => s.sort_with(options).map(Self::from),
1748 Column::Scalar(s) => {
1749 _ = s.as_single_value_series().sort_with(options)?;
1751
1752 Ok(self.clone())
1753 },
1754 }
1755 }
1756
1757 pub fn map_unary_elementwise_to_bool(
1758 &self,
1759 f: impl Fn(&Series) -> BooleanChunked,
1760 ) -> BooleanChunked {
1761 self.try_map_unary_elementwise_to_bool(|s| Ok(f(s)))
1762 .unwrap()
1763 }
1764 pub fn try_map_unary_elementwise_to_bool(
1765 &self,
1766 f: impl Fn(&Series) -> PolarsResult<BooleanChunked>,
1767 ) -> PolarsResult<BooleanChunked> {
1768 match self {
1769 Column::Series(s) => f(s),
1770 Column::Scalar(s) => Ok(f(&s.as_single_value_series())?.new_from_index(0, s.len())),
1771 }
1772 }
1773
1774 pub fn apply_unary_elementwise(&self, f: impl Fn(&Series) -> Series) -> Column {
1775 self.try_apply_unary_elementwise(|s| Ok(f(s))).unwrap()
1776 }
1777 pub fn try_apply_unary_elementwise(
1778 &self,
1779 f: impl Fn(&Series) -> PolarsResult<Series>,
1780 ) -> PolarsResult<Column> {
1781 match self {
1782 Column::Series(s) => f(s).map(Column::from),
1783 Column::Scalar(s) => Ok(ScalarColumn::from_single_value_series(
1784 f(&s.as_single_value_series())?,
1785 s.len(),
1786 )
1787 .into()),
1788 }
1789 }
1790
1791 pub fn apply_broadcasting_binary_elementwise(
1792 &self,
1793 other: &Self,
1794 op: impl Fn(&Series, &Series) -> Series,
1795 ) -> PolarsResult<Column> {
1796 self.try_apply_broadcasting_binary_elementwise(other, |lhs, rhs| Ok(op(lhs, rhs)))
1797 }
1798 pub fn try_apply_broadcasting_binary_elementwise(
1799 &self,
1800 other: &Self,
1801 op: impl Fn(&Series, &Series) -> PolarsResult<Series>,
1802 ) -> PolarsResult<Column> {
1803 let length = broadcast_len([self, other])
1805 .context("cannot do a binary operation on columns of different lengths")?;
1806 match (self, other) {
1807 (Column::Series(lhs), Column::Series(rhs)) => op(lhs, rhs).map(Column::from),
1808 (Column::Series(lhs), Column::Scalar(rhs)) => {
1809 op(lhs, &rhs.as_single_value_series()).map(Column::from)
1810 },
1811 (Column::Scalar(lhs), Column::Series(rhs)) => {
1812 op(&lhs.as_single_value_series(), rhs).map(Column::from)
1813 },
1814 (Column::Scalar(lhs), Column::Scalar(rhs)) => {
1815 let lhs = lhs.as_single_value_series();
1816 let rhs = rhs.as_single_value_series();
1817
1818 Ok(ScalarColumn::from_single_value_series(op(&lhs, &rhs)?, length).into_column())
1819 },
1820 }
1821 }
1822
1823 pub fn apply_binary_elementwise(
1824 &self,
1825 other: &Self,
1826 f: impl Fn(&Series, &Series) -> Series,
1827 f_lb: impl Fn(&Scalar, &Series) -> Series,
1828 f_rb: impl Fn(&Series, &Scalar) -> Series,
1829 ) -> Column {
1830 self.try_apply_binary_elementwise(
1831 other,
1832 |lhs, rhs| Ok(f(lhs, rhs)),
1833 |lhs, rhs| Ok(f_lb(lhs, rhs)),
1834 |lhs, rhs| Ok(f_rb(lhs, rhs)),
1835 )
1836 .unwrap()
1837 }
1838 pub fn try_apply_binary_elementwise(
1839 &self,
1840 other: &Self,
1841 f: impl Fn(&Series, &Series) -> PolarsResult<Series>,
1842 f_lb: impl Fn(&Scalar, &Series) -> PolarsResult<Series>,
1843 f_rb: impl Fn(&Series, &Scalar) -> PolarsResult<Series>,
1844 ) -> PolarsResult<Column> {
1845 debug_assert_eq!(self.len(), other.len());
1846
1847 match (self, other) {
1848 (Column::Series(lhs), Column::Series(rhs)) => f(lhs, rhs).map(Column::from),
1849 (Column::Series(lhs), Column::Scalar(rhs)) => f_rb(lhs, rhs.scalar()).map(Column::from),
1850 (Column::Scalar(lhs), Column::Series(rhs)) => f_lb(lhs.scalar(), rhs).map(Column::from),
1851 (Column::Scalar(lhs), Column::Scalar(rhs)) => {
1852 let lhs = lhs.as_single_value_series();
1853 let rhs = rhs.as_single_value_series();
1854
1855 Ok(
1856 ScalarColumn::from_single_value_series(f(&lhs, &rhs)?, self.len())
1857 .into_column(),
1858 )
1859 },
1860 }
1861 }
1862
1863 #[cfg(feature = "approx_unique")]
1864 pub fn approx_n_unique(&self) -> PolarsResult<IdxSize> {
1865 match self {
1866 Column::Series(s) => s.approx_n_unique(),
1867 Column::Scalar(s) => {
1868 s.as_single_value_series().approx_n_unique()?;
1870 Ok(1)
1871 },
1872 }
1873 }
1874
1875 pub fn n_chunks(&self) -> usize {
1876 match self {
1877 Column::Series(s) => s.n_chunks(),
1878 Column::Scalar(s) => s.lazy_as_materialized_series().map_or(1, |x| x.n_chunks()),
1879 }
1880 }
1881
1882 #[expect(clippy::wrong_self_convention)]
1883 pub(crate) fn into_total_ord_inner<'a>(&'a self) -> Box<dyn TotalOrdInner + 'a> {
1884 self.as_materialized_series().into_total_ord_inner()
1886 }
1887 #[expect(unused, clippy::wrong_self_convention)]
1888 pub(crate) fn into_total_eq_inner<'a>(&'a self) -> Box<dyn TotalEqInner + 'a> {
1889 self.as_materialized_series().into_total_eq_inner()
1891 }
1892
1893 pub fn rechunk_to_arrow(self, compat_level: CompatLevel) -> Box<dyn Array> {
1894 let mut series = self.take_materialized_series();
1896 if series.n_chunks() > 1 {
1897 series = series.rechunk();
1898 }
1899 series.to_arrow(0, compat_level)
1900 }
1901
1902 pub fn trim_lists_to_normalized_offsets(&self) -> Option<Column> {
1903 self.as_materialized_series()
1904 .trim_lists_to_normalized_offsets()
1905 .map(Column::from)
1906 }
1907
1908 pub fn propagate_nulls(&self) -> Option<Column> {
1909 self.as_materialized_series()
1910 .propagate_nulls()
1911 .map(Column::from)
1912 }
1913
1914 pub fn deposit(&self, validity: &Bitmap) -> Column {
1915 self.as_materialized_series()
1916 .deposit(validity)
1917 .into_column()
1918 }
1919
1920 pub fn rechunk_validity(&self) -> Option<Bitmap> {
1921 self.as_materialized_series().rechunk_validity()
1923 }
1924
1925 pub fn unique_id(&self) -> PolarsResult<(IdxSize, Vec<IdxSize>)> {
1926 self.as_materialized_series().unique_id()
1927 }
1928}
1929
1930impl Default for Column {
1931 fn default() -> Self {
1932 Self::new_scalar(
1933 PlSmallStr::EMPTY,
1934 Scalar::new(DataType::Int64, AnyValue::Null),
1935 0,
1936 )
1937 }
1938}
1939
1940impl PartialEq for Column {
1941 fn eq(&self, other: &Self) -> bool {
1942 self.as_materialized_series()
1944 .eq(other.as_materialized_series())
1945 }
1946}
1947
1948impl From<Series> for Column {
1949 #[inline]
1950 fn from(series: Series) -> Self {
1951 if series.len() == 1 {
1954 return Self::Scalar(ScalarColumn::unit_scalar_from_series(series));
1955 }
1956
1957 Self::Series(SeriesColumn::new(series))
1958 }
1959}
1960
1961impl<T: IntoSeries> IntoColumn for T {
1962 #[inline]
1963 fn into_column(self) -> Column {
1964 self.into_series().into()
1965 }
1966}
1967
1968impl IntoColumn for Column {
1969 #[inline(always)]
1970 fn into_column(self) -> Column {
1971 self
1972 }
1973}
1974
1975impl BroadcastLength for Column {
1976 fn _broadcast_len(&self) -> usize {
1977 self.len()
1978 }
1979
1980 fn _column_name(&self) -> Option<&str> {
1981 Some(self.name())
1982 }
1983}
1984
1985#[derive(Clone)]
1990#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1991#[cfg_attr(feature = "serde", serde(into = "Series"))]
1992struct _SerdeSeries(Series);
1993
1994impl From<Column> for _SerdeSeries {
1995 #[inline]
1996 fn from(value: Column) -> Self {
1997 Self(value.take_materialized_series())
1998 }
1999}
2000
2001impl From<_SerdeSeries> for Series {
2002 #[inline]
2003 fn from(value: _SerdeSeries) -> Self {
2004 value.0
2005 }
2006}