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