1#![allow(unsafe_op_in_unsafe_fn)]
3use std::sync::Arc;
4
5use arrow::array::*;
6use arrow::bitmap::Bitmap;
7use arrow::compute::concatenate::concatenate_unchecked;
8use arrow::compute::utils::combine_validities_and;
9use polars_compute::filter::filter_with_bitmap;
10
11use crate::prelude::{ChunkTakeUnchecked, *};
12
13pub mod ops;
14#[macro_use]
15pub mod arithmetic;
16pub mod builder;
17pub mod cast;
18pub mod collect;
19pub mod comparison;
20pub mod flags;
21pub mod float;
22pub mod iterator;
23#[cfg(feature = "ndarray")]
24pub(crate) mod ndarray;
25
26pub mod arg_min_max;
27#[cfg(feature = "dtype-array")]
28pub(crate) mod array;
29mod binary;
30mod binary_offset;
31mod bitwise;
32#[cfg(feature = "object")]
33mod drop;
34mod from;
35mod from_iterator;
36pub mod from_iterator_par;
37pub(crate) mod list;
38pub(crate) mod logical;
39#[cfg(feature = "object")]
40pub mod object;
41#[cfg(feature = "random")]
42mod random;
43#[cfg(feature = "dtype-struct")]
44mod struct_;
45#[cfg(any(
46 feature = "temporal",
47 feature = "dtype-datetime",
48 feature = "dtype-date"
49))]
50pub mod temporal;
51mod to_vec;
52mod trusted_len;
53pub(crate) use arg_min_max::*;
54#[cfg(feature = "dtype-struct")]
55pub use struct_::StructChunked;
56
57use self::flags::{StatisticsFlags, StatisticsFlagsIM};
58use crate::series::IsSorted;
59use crate::utils::{first_non_null, first_null, last_non_null};
60
61pub type ChunkLenIter<'a> = std::iter::Map<std::slice::Iter<'a, ArrayRef>, fn(&ArrayRef) -> usize>;
62
63pub struct ChunkedArray<T: PolarsDataType> {
138 pub(crate) field: Arc<Field>,
139 pub(crate) chunks: Vec<ArrayRef>,
140
141 pub(crate) flags: StatisticsFlagsIM,
142
143 length: usize,
144 null_count: usize,
145 _pd: std::marker::PhantomData<T>,
146}
147
148impl<T: PolarsDataType> ChunkedArray<T> {
149 fn should_rechunk(&self) -> bool {
150 self.chunks.len() > 1 && self.chunks.len() > self.len() / 3
151 }
152
153 fn optional_rechunk(mut self) -> Self {
154 if self.should_rechunk() {
156 self.rechunk_mut()
157 }
158 self
159 }
160
161 pub(crate) fn as_any(&self) -> &dyn std::any::Any {
162 self
163 }
164
165 pub fn unpack_series_matching_type<'a>(
167 &self,
168 series: &'a Series,
169 ) -> PolarsResult<&'a ChunkedArray<T>> {
170 polars_ensure!(
171 self.dtype() == series.dtype(),
172 SchemaMismatch: "cannot unpack series of type `{}` into `{}`",
173 series.dtype(),
174 self.dtype(),
175 );
176
177 Ok(unsafe { self.unpack_series_matching_physical_type(series) })
179 }
180
181 fn new_with_compute_len(field: Arc<Field>, chunks: Vec<ArrayRef>) -> Self {
186 unsafe {
187 let mut chunked_arr = Self::new_with_dims(field, chunks, 0, 0);
188 chunked_arr.compute_len();
189 chunked_arr
190 }
191 }
192
193 pub unsafe fn new_with_dims(
197 field: Arc<Field>,
198 chunks: Vec<ArrayRef>,
199 length: usize,
200 null_count: usize,
201 ) -> Self {
202 Self {
203 field,
204 chunks,
205 flags: StatisticsFlagsIM::empty(),
206
207 _pd: Default::default(),
208 length,
209 null_count,
210 }
211 }
212
213 pub(crate) fn is_sorted_ascending_flag(&self) -> bool {
214 self.get_flags().is_sorted_ascending()
215 }
216
217 pub(crate) fn is_sorted_descending_flag(&self) -> bool {
218 self.get_flags().is_sorted_descending()
219 }
220
221 pub(crate) fn is_sorted_any(&self) -> bool {
223 self.get_flags().is_sorted_any()
224 }
225
226 pub fn unset_fast_explode_list(&mut self) {
227 self.set_fast_explode_list(false)
228 }
229
230 pub fn set_fast_explode_list(&mut self, value: bool) {
231 let mut flags = self.flags.get_mut();
232 flags.set(StatisticsFlags::CAN_FAST_EXPLODE_LIST, value);
233 self.flags.set_mut(flags);
234 }
235
236 pub fn get_fast_explode_list(&self) -> bool {
237 self.get_flags().can_fast_explode_list()
238 }
239
240 pub fn get_flags(&self) -> StatisticsFlags {
241 self.flags.get()
242 }
243
244 pub fn set_flags(&mut self, flags: StatisticsFlags) {
246 self.flags = StatisticsFlagsIM::new(flags);
247 }
248
249 pub fn is_sorted_flag(&self) -> IsSorted {
250 self.get_flags().is_sorted()
251 }
252
253 pub fn retain_flags_from<U: PolarsDataType>(
254 &mut self,
255 from: &ChunkedArray<U>,
256 retain_flags: StatisticsFlags,
257 ) {
258 let flags = from.flags.get();
259 if !flags.is_empty() {
261 self.set_flags(flags & retain_flags)
262 }
263 }
264
265 pub fn set_sorted_flag(&mut self, sorted: IsSorted) {
267 let mut flags = self.flags.get_mut();
268 flags.set_sorted(sorted);
269 self.flags.set_mut(flags);
270 }
271
272 pub fn with_sorted_flag(&self, sorted: IsSorted) -> Self {
274 let mut out = self.clone();
275 out.set_sorted_flag(sorted);
276 out
277 }
278
279 pub fn first_null(&self) -> Option<usize> {
280 if self.null_count() == 0 {
281 None
282 }
283 else if self.null_count() == self.len() {
285 Some(0)
286 } else if self.is_sorted_any() {
287 let out = if self
288 .chunks
289 .iter()
290 .find(|arr| !arr.is_empty())
291 .unwrap()
292 .is_null(0)
293 {
294 0
296 } else {
297 self.null_count()
299 };
300
301 debug_assert!(
302 unsafe { self.get_unchecked(out) }.is_some(),
304 "incorrect sorted flag"
305 );
306
307 Some(out)
308 } else {
309 first_null(self.chunks().iter().map(|arr| arr.as_ref()))
310 }
311 }
312
313 pub fn first_non_null(&self) -> Option<usize> {
315 if self.null_count() == self.len() {
316 None
317 }
318 else if self.null_count() == 0 {
320 Some(0)
321 } else if self.is_sorted_any() {
322 let out = if self
323 .chunks
324 .iter()
325 .find(|arr| !arr.is_empty())
326 .unwrap()
327 .is_null(0)
328 {
329 self.null_count()
331 } else {
332 0
334 };
335
336 debug_assert!(
337 unsafe { self.get_unchecked(out) }.is_some(),
339 "incorrect sorted flag"
340 );
341
342 Some(out)
343 } else {
344 first_non_null(self.chunks().iter().map(|arr| arr.as_ref()))
345 }
346 }
347
348 pub fn last_non_null(&self) -> Option<usize> {
350 if self.null_count() == self.len() {
351 None
352 }
353 else if self.null_count() == 0 {
355 Some(self.len() - 1)
356 } else if self.is_sorted_any() {
357 let out = if self
358 .chunks
359 .iter()
360 .find(|arr| !arr.is_empty())
361 .unwrap()
362 .is_null(0)
363 {
364 self.len() - 1
366 } else {
367 self.len() - self.null_count() - 1
369 };
370
371 debug_assert!(
372 unsafe { self.get_unchecked(out) }.is_some(),
374 "incorrect sorted flag"
375 );
376
377 Some(out)
378 } else {
379 last_non_null(self.chunks().iter().map(|arr| arr.as_ref()), self.len())
380 }
381 }
382
383 pub fn drop_nulls(&self) -> Self {
384 if self.null_count() == 0 {
385 self.clone()
386 } else {
387 let chunks = self
388 .downcast_iter()
389 .map(|arr| {
390 if arr.null_count() == 0 {
391 arr.to_boxed()
392 } else {
393 filter_with_bitmap(arr, arr.validity().unwrap())
394 }
395 })
396 .collect();
397 unsafe {
398 Self::new_with_dims(
399 self.field.clone(),
400 chunks,
401 self.len() - self.null_count(),
402 0,
403 )
404 }
405 }
406 }
407
408 #[inline]
410 #[allow(clippy::type_complexity)]
411 pub fn iter_validities(
412 &self,
413 ) -> impl ExactSizeIterator<Item = Option<&Bitmap>> + DoubleEndedIterator {
414 fn to_validity(arr: &ArrayRef) -> Option<&Bitmap> {
415 arr.validity()
416 }
417 self.chunks.iter().map(to_validity)
418 }
419
420 #[inline]
421 pub fn has_nulls(&self) -> bool {
423 self.null_count > 0
424 }
425
426 pub fn shrink_to_fit(&mut self) {
428 self.chunks = vec![concatenate_unchecked(self.chunks.as_slice()).unwrap()];
429 }
430
431 pub fn clear(&self) -> Self {
432 let mut ca = unsafe {
434 self.copy_with_chunks(vec![new_empty_array(
435 self.chunks.first().unwrap().dtype().clone(),
436 )])
437 };
438
439 use StatisticsFlags as F;
440 ca.retain_flags_from(self, F::IS_SORTED_ANY | F::CAN_FAST_EXPLODE_LIST);
441 ca
442 }
443
444 pub(crate) unsafe fn unpack_series_matching_physical_type<'a>(
451 &self,
452 series: &'a Series,
453 ) -> &'a ChunkedArray<T> {
454 let series_trait = &**series;
455 if self.dtype() == series.dtype() {
456 &*(series_trait as *const dyn SeriesTrait as *const ChunkedArray<T>)
457 } else {
458 use DataType::*;
459 match (self.dtype(), series.dtype()) {
460 (Int64, Datetime(_, _)) | (Int64, Duration(_)) | (Int32, Date) => {
461 &*(series_trait as *const dyn SeriesTrait as *const ChunkedArray<T>)
462 },
463 _ => panic!(
464 "cannot unpack series {:?} into matching type {:?}",
465 series,
466 self.dtype()
467 ),
468 }
469 }
470 }
471
472 pub fn chunk_lengths(&self) -> ChunkLenIter<'_> {
474 self.chunks.iter().map(|chunk| chunk.len())
475 }
476
477 #[inline]
479 pub fn chunks(&self) -> &Vec<ArrayRef> {
480 &self.chunks
481 }
482
483 #[inline]
489 pub unsafe fn chunks_mut(&mut self) -> &mut Vec<ArrayRef> {
490 &mut self.chunks
491 }
492
493 pub fn is_optimal_aligned(&self) -> bool {
495 self.chunks.len() == 1 && self.null_count() == 0
496 }
497
498 unsafe fn copy_with_chunks(&self, chunks: Vec<ArrayRef>) -> Self {
503 Self::new_with_compute_len(self.field.clone(), chunks)
504 }
505
506 pub fn dtype(&self) -> &DataType {
508 self.field.dtype()
509 }
510
511 pub(crate) unsafe fn set_dtype(&mut self, dtype: DataType) {
512 self.field = Arc::new(Field::new(self.name().clone(), dtype))
513 }
514
515 pub fn name(&self) -> &PlSmallStr {
517 self.field.name()
518 }
519
520 pub fn ref_field(&self) -> &Field {
522 &self.field
523 }
524
525 pub fn rename(&mut self, name: PlSmallStr) {
527 self.field = Arc::new(Field::new(name, self.field.dtype().clone()));
528 }
529
530 pub fn with_name(mut self, name: PlSmallStr) -> Self {
532 self.rename(name);
533 self
534 }
535}
536
537impl<T> ChunkedArray<T>
538where
539 T: PolarsDataType,
540{
541 #[inline]
547 pub fn get(&self, idx: usize) -> Option<T::Physical<'_>> {
548 let (chunk_idx, arr_idx) = self.index_to_chunked_index(idx);
549 assert!(
550 chunk_idx < self.chunks().len(),
551 "index: {} out of bounds for len: {}",
552 idx,
553 self.len()
554 );
555 unsafe {
556 let arr = self.downcast_get_unchecked(chunk_idx);
557 assert!(
558 arr_idx < arr.len(),
559 "index: {} out of bounds for len: {}",
560 idx,
561 self.len()
562 );
563 arr.get_unchecked(arr_idx)
564 }
565 }
566
567 #[inline]
573 pub unsafe fn get_unchecked(&self, idx: usize) -> Option<T::Physical<'_>> {
574 let (chunk_idx, arr_idx) = self.index_to_chunked_index(idx);
575
576 unsafe {
577 self.downcast_get_unchecked(chunk_idx)
579 .get_unchecked(arr_idx)
580 }
581 }
582
583 #[inline]
589 pub unsafe fn value_unchecked(&self, idx: usize) -> T::Physical<'_> {
590 let (chunk_idx, arr_idx) = self.index_to_chunked_index(idx);
591
592 unsafe {
593 self.downcast_get_unchecked(chunk_idx)
595 .value_unchecked(arr_idx)
596 }
597 }
598
599 #[inline]
602 pub fn first(&self) -> Option<T::Physical<'_>> {
603 self.iter().next().unwrap()
604 }
605
606 #[inline]
609 pub fn last(&self) -> Option<T::Physical<'_>> {
610 let arr = self
611 .downcast_iter()
612 .rev()
613 .find(|arr| !arr.is_empty())
614 .unwrap();
615 unsafe { arr.get_unchecked(arr.len() - 1) }
616 }
617
618 pub fn set_validity(&mut self, validity: Option<Bitmap>) {
619 assert!(
620 !self.dtype().is_struct(),
621 "set_outer_validity should be used for struct types"
622 );
623 if let Some(v) = &validity {
624 assert_eq!(self.len(), v.len());
625 }
626 let mut i = 0;
627 for chunk in unsafe { self.chunks_mut() } {
628 *chunk =
629 chunk.with_validity(validity.as_ref().map(|v| v.clone().sliced(i, chunk.len())));
630 i += chunk.len();
631 }
632 self.null_count = validity.map(|v| v.unset_bits()).unwrap_or(0);
633 self.set_fast_explode_list(false);
634 }
635
636 pub fn with_validity(mut self, validity: Option<Bitmap>) -> Self {
637 self.set_validity(validity);
638 self
639 }
640}
641
642impl<T> ChunkedArray<T>
643where
644 T: PolarsDataType,
645 ChunkedArray<T>: ChunkTakeUnchecked<[IdxSize]>,
646{
647 pub fn deposit(&self, validity: &Bitmap) -> Self {
649 let set_bits = validity.set_bits();
650
651 assert_eq!(self.len(), set_bits);
652
653 if set_bits == validity.len() {
654 return self.clone();
655 }
656
657 if set_bits == 0 {
658 return Self::full_null_like(self, validity.len());
659 }
660
661 let mut null_mask = validity.clone();
662
663 let mut gather_idxs = Vec::with_capacity(validity.len());
664 let leading_nulls = null_mask.take_leading_zeros();
665 gather_idxs.extend(std::iter::repeat_n(0, leading_nulls + 1));
666
667 let mut i = 0 as IdxSize;
668 gather_idxs.extend(null_mask.iter().skip(1).map(|v| {
669 i += IdxSize::from(v);
670 i
671 }));
672
673 let mut ca = unsafe { ChunkTakeUnchecked::take_unchecked(self, &gather_idxs) };
674 ca.set_validity(combine_validities_and(
675 Some(validity),
676 ca.rechunk_validity().as_ref(),
677 ));
678 ca
679 }
680}
681
682impl ListChunked {
683 #[inline]
684 pub fn get_as_series(&self, idx: usize) -> Option<Series> {
685 unsafe {
686 Some(Series::from_chunks_and_dtype_unchecked(
687 self.name().clone(),
688 vec![self.get(idx)?],
689 &self.inner_dtype().to_physical(),
690 ))
691 }
692 }
693
694 pub fn has_empty_lists(&self) -> bool {
695 for arr in self.downcast_iter() {
696 if arr.is_empty() {
697 continue;
698 }
699
700 if match arr.validity() {
701 None => arr.offsets().lengths().any(|l| l == 0),
702 Some(validity) => arr
703 .offsets()
704 .lengths()
705 .enumerate()
706 .any(|(i, l)| l == 0 && unsafe { validity.get_bit_unchecked(i) }),
707 } {
708 return true;
709 }
710 }
711
712 false
713 }
714
715 pub fn has_masked_out_values(&self) -> bool {
716 for arr in self.downcast_iter() {
717 if arr.is_empty() {
718 continue;
719 }
720
721 if *arr.offsets().first() != 0 || *arr.offsets().last() != arr.values().len() as i64 {
722 return true;
723 }
724
725 let Some(validity) = arr.validity() else {
726 continue;
727 };
728 if validity.set_bits() == 0 {
729 continue;
730 }
731
732 for i in (!validity).true_idx_iter() {
734 if arr.offsets().length_at(i) > 0 {
735 return true;
736 }
737 }
738 }
739
740 false
741 }
742}
743
744#[cfg(feature = "dtype-array")]
745impl ArrayChunked {
746 #[inline]
747 pub fn get_as_series(&self, idx: usize) -> Option<Series> {
748 unsafe {
749 Some(Series::from_chunks_and_dtype_unchecked(
750 self.name().clone(),
751 vec![self.get(idx)?],
752 &self.inner_dtype().to_physical(),
753 ))
754 }
755 }
756
757 pub fn from_aligned_values(
758 name: PlSmallStr,
759 inner_dtype: &DataType,
760 width: usize,
761 chunks: Vec<ArrayRef>,
762 length: usize,
763 ) -> Self {
764 let dtype = DataType::Array(Box::new(inner_dtype.clone()), width);
765 let arrow_dtype = inner_dtype
766 .to_physical()
767 .to_arrow(CompatLevel::newest())
768 .to_fixed_size_list(width, true);
769 let field = Arc::new(Field::new(name, dtype));
770 if width == 0 {
771 use arrow::array::builder::{ArrayBuilder, make_builder};
772 let values = make_builder(&inner_dtype.to_arrow(CompatLevel::newest())).freeze();
773 return ArrayChunked::new_with_compute_len(
774 field,
775 vec![FixedSizeListArray::new(arrow_dtype, length, values, None).into_boxed()],
776 );
777 }
778 let mut total_len = 0;
779 let chunks = chunks
780 .iter()
781 .map(|chunk| {
782 debug_assert_eq!(chunk.len() % width, 0);
783 let chunk_len = chunk.len() / width;
784 total_len += chunk_len;
785 FixedSizeListArray::new(arrow_dtype.clone(), chunk_len, chunk.clone(), None)
786 .into_boxed()
787 })
788 .collect();
789 debug_assert_eq!(total_len, length);
790
791 unsafe { Self::new_with_dims(field, chunks, length, 0) }
792 }
793
794 pub fn to_list(&self) -> ListChunked {
798 let inner_dtype = self.inner_dtype();
799 let chunks = self
800 .downcast_iter()
801 .map(|chunk| {
802 use arrow::offset::OffsetsBuffer;
803
804 let inner_dtype = chunk.dtype().inner_dtype().unwrap();
805 let dtype = inner_dtype.clone().to_large_list(true);
806
807 let offsets = (0..=chunk.len())
808 .map(|i| (i * self.width()) as i64)
809 .collect::<Vec<i64>>();
810
811 let offsets = unsafe { OffsetsBuffer::new_unchecked(offsets.into()) };
813
814 ListArray::<i64>::new(
815 dtype,
816 offsets,
817 chunk.values().clone(),
818 chunk.validity().cloned(),
819 )
820 .into_boxed()
821 })
822 .collect();
823
824 let mut ca = unsafe {
826 ListChunked::new_with_dims(
827 Arc::new(Field::new(
828 self.name().clone(),
829 DataType::List(Box::new(inner_dtype.clone())),
830 )),
831 chunks,
832 self.len(),
833 self.null_count(),
834 )
835 };
836 ca.set_fast_explode_list(!self.has_nulls());
837 ca
838 }
839}
840
841impl<T> ChunkedArray<T>
842where
843 T: PolarsDataType,
844{
845 pub fn match_chunks<I>(&self, chunk_id: I) -> Self
849 where
850 I: Iterator<Item = usize>,
851 {
852 debug_assert!(self.chunks.len() == 1);
853 let slice = |ca: &Self| {
855 let array = &ca.chunks[0];
856
857 let mut offset = 0;
858 let chunks = chunk_id
859 .map(|len| {
860 debug_assert!((offset + len) <= array.len());
862 let out = unsafe { array.sliced_unchecked(offset, len) };
863 offset += len;
864 out
865 })
866 .collect();
867
868 debug_assert_eq!(offset, array.len());
869
870 unsafe {
872 Self::from_chunks_and_dtype(self.name().clone(), chunks, self.dtype().clone())
873 }
874 };
875
876 if self.chunks.len() != 1 {
877 let out = self.rechunk();
878 slice(&out)
879 } else {
880 slice(self)
881 }
882 }
883}
884
885impl<T: PolarsDataType> AsRefDataType for ChunkedArray<T> {
886 fn as_ref_dtype(&self) -> &DataType {
887 self.dtype()
888 }
889}
890
891pub(crate) trait AsSinglePtr: AsRefDataType {
892 fn as_single_ptr(&mut self) -> PolarsResult<usize> {
894 polars_bail!(opq = as_single_ptr, self.as_ref_dtype());
895 }
896}
897
898impl<T> AsSinglePtr for ChunkedArray<T>
899where
900 T: PolarsNumericType,
901{
902 fn as_single_ptr(&mut self) -> PolarsResult<usize> {
903 self.rechunk_mut();
904 let a = self.data_views().next().unwrap();
905 let ptr = a.as_ptr();
906 Ok(ptr as usize)
907 }
908}
909
910impl AsSinglePtr for BooleanChunked {}
911impl AsSinglePtr for ListChunked {}
912#[cfg(feature = "dtype-array")]
913impl AsSinglePtr for ArrayChunked {}
914impl AsSinglePtr for StringChunked {}
915impl AsSinglePtr for BinaryChunked {}
916#[cfg(feature = "object")]
917impl<T: PolarsObject> AsSinglePtr for ObjectChunked<T> {}
918
919pub enum ChunkedArrayLayout<'a, T: PolarsDataType> {
920 SingleNoNull(&'a T::Array),
921 Single(&'a T::Array),
922 MultiNoNull(&'a ChunkedArray<T>),
923 Multi(&'a ChunkedArray<T>),
924}
925
926impl<T> ChunkedArray<T>
927where
928 T: PolarsDataType,
929{
930 pub fn layout(&self) -> ChunkedArrayLayout<'_, T> {
931 if self.chunks.len() == 1 {
932 let arr = self.downcast_iter().next().unwrap();
933 return if arr.null_count() == 0 {
934 ChunkedArrayLayout::SingleNoNull(arr)
935 } else {
936 ChunkedArrayLayout::Single(arr)
937 };
938 }
939
940 if self.downcast_iter().all(|a| a.null_count() == 0) {
941 ChunkedArrayLayout::MultiNoNull(self)
942 } else {
943 ChunkedArrayLayout::Multi(self)
944 }
945 }
946}
947
948impl<T> ChunkedArray<T>
949where
950 T: PolarsNumericType,
951{
952 pub fn cont_slice(&self) -> PolarsResult<&[T::Native]> {
954 polars_ensure!(
955 self.chunks.len() == 1 && self.chunks[0].null_count() == 0,
956 ComputeError: "chunked array is not contiguous"
957 );
958 Ok(self.downcast_iter().next().map(|arr| arr.values()).unwrap())
959 }
960
961 pub(crate) fn cont_slice_mut(&mut self) -> Option<&mut [T::Native]> {
963 if self.chunks.len() == 1 && self.chunks[0].null_count() == 0 {
964 let arr = unsafe { self.downcast_iter_mut().next().unwrap() };
966 arr.get_mut_values()
967 } else {
968 None
969 }
970 }
971
972 pub fn data_views(&self) -> impl DoubleEndedIterator<Item = &[T::Native]> {
976 self.downcast_iter().map(|arr| arr.values().as_slice())
977 }
978
979 #[allow(clippy::wrong_self_convention)]
980 pub fn into_no_null_iter(
981 &self,
982 ) -> impl '_ + Send + Sync + ExactSizeIterator<Item = T::Native> + DoubleEndedIterator + TrustedLen
983 {
984 #[allow(clippy::map_clone)]
986 unsafe {
988 self.data_views()
989 .flatten()
990 .map(|v| *v)
991 .trust_my_length(self.len())
992 }
993 }
994}
995
996impl<T: PolarsDataType> Clone for ChunkedArray<T> {
997 fn clone(&self) -> Self {
998 ChunkedArray {
999 field: self.field.clone(),
1000 chunks: self.chunks.clone(),
1001 flags: self.flags.clone(),
1002
1003 _pd: Default::default(),
1004 length: self.length,
1005 null_count: self.null_count,
1006 }
1007 }
1008}
1009
1010impl<T: PolarsDataType> AsRef<ChunkedArray<T>> for ChunkedArray<T> {
1011 fn as_ref(&self) -> &ChunkedArray<T> {
1012 self
1013 }
1014}
1015
1016impl ValueSize for ListChunked {
1017 fn get_values_size(&self) -> usize {
1018 self.chunks
1019 .iter()
1020 .fold(0usize, |acc, arr| acc + arr.get_values_size())
1021 }
1022}
1023
1024#[cfg(feature = "dtype-array")]
1025impl ValueSize for ArrayChunked {
1026 fn get_values_size(&self) -> usize {
1027 self.chunks
1028 .iter()
1029 .fold(0usize, |acc, arr| acc + arr.get_values_size())
1030 }
1031}
1032impl ValueSize for StringChunked {
1033 fn get_values_size(&self) -> usize {
1034 self.chunks
1035 .iter()
1036 .fold(0usize, |acc, arr| acc + arr.get_values_size())
1037 }
1038}
1039
1040impl ValueSize for BinaryOffsetChunked {
1041 fn get_values_size(&self) -> usize {
1042 self.chunks
1043 .iter()
1044 .fold(0usize, |acc, arr| acc + arr.get_values_size())
1045 }
1046}
1047
1048pub(crate) fn to_primitive<T: PolarsNumericType>(
1049 values: Vec<T::Native>,
1050 validity: Option<Bitmap>,
1051) -> PrimitiveArray<T::Native> {
1052 PrimitiveArray::new(
1053 T::get_static_dtype().to_arrow(CompatLevel::newest()),
1054 values.into(),
1055 validity,
1056 )
1057}
1058
1059pub(crate) fn to_array<T: PolarsNumericType>(
1060 values: Vec<T::Native>,
1061 validity: Option<Bitmap>,
1062) -> ArrayRef {
1063 Box::new(to_primitive::<T>(values, validity))
1064}
1065
1066impl<T: PolarsDataType> Default for ChunkedArray<T> {
1067 fn default() -> Self {
1068 let dtype = T::get_static_dtype();
1069 let arrow_dtype = dtype.to_physical().to_arrow(CompatLevel::newest());
1070 ChunkedArray {
1071 field: Arc::new(Field::new(PlSmallStr::EMPTY, dtype)),
1072 chunks: vec![new_empty_array(arrow_dtype)],
1074 flags: StatisticsFlagsIM::empty(),
1075
1076 _pd: Default::default(),
1077 length: 0,
1078 null_count: 0,
1079 }
1080 }
1081}
1082
1083#[cfg(test)]
1084pub(crate) mod test {
1085 use crate::prelude::*;
1086
1087 pub(crate) fn get_chunked_array() -> Int32Chunked {
1088 ChunkedArray::new(PlSmallStr::from_static("a"), &[1, 2, 3])
1089 }
1090
1091 #[test]
1092 fn test_sort() {
1093 let a = Int32Chunked::new(PlSmallStr::from_static("a"), &[1, 9, 3, 2]);
1094 let b = a
1095 .sort(false)
1096 .iter()
1097 .map(|opt| opt.unwrap())
1098 .collect::<Vec<_>>();
1099 assert_eq!(b, [1, 2, 3, 9]);
1100 let a = StringChunked::new(PlSmallStr::from_static("a"), &["b", "a", "c"]);
1101 let a = a.sort(false);
1102 let b = a.iter().collect::<Vec<_>>();
1103 assert_eq!(b, [Some("a"), Some("b"), Some("c")]);
1104 assert!(a.is_sorted_ascending_flag());
1105 }
1106
1107 #[test]
1108 fn arithmetic() {
1109 let a = &Int32Chunked::new(PlSmallStr::from_static("a"), &[1, 100, 6, 40]);
1110 let b = &Int32Chunked::new(PlSmallStr::from_static("b"), &[-1, 2, 3, 4]);
1111
1112 println!("{:?}", a + b);
1115 println!("{:?}", a - b);
1116 println!("{:?}", a * b);
1117 println!("{:?}", a / b);
1118 }
1119
1120 #[test]
1121 fn iter() {
1122 let s1 = get_chunked_array();
1123 assert_eq!(s1.iter().fold(0, |acc, val| { acc + val.unwrap() }), 6)
1125 }
1126
1127 #[test]
1128 fn limit() {
1129 let a = get_chunked_array();
1130 let b = a.limit(2);
1131 println!("{b:?}");
1132 assert_eq!(b.len(), 2)
1133 }
1134
1135 #[test]
1136 fn filter() {
1137 let a = get_chunked_array();
1138 let b = a
1139 .filter(&BooleanChunked::new(
1140 PlSmallStr::from_static("filter"),
1141 &[true, false, false],
1142 ))
1143 .unwrap();
1144 assert_eq!(b.len(), 1);
1145 assert_eq!(b.iter().next(), Some(Some(1)));
1146 }
1147
1148 #[test]
1149 fn aggregates() {
1150 let a = &Int32Chunked::new(PlSmallStr::from_static("a"), &[1, 100, 10, 9]);
1151 assert_eq!(a.max(), Some(100));
1152 assert_eq!(a.min(), Some(1));
1153 assert_eq!(a.sum(), Some(120))
1154 }
1155
1156 #[test]
1157 fn take() {
1158 let a = get_chunked_array();
1159 let new = a.take(&[0 as IdxSize, 1]).unwrap();
1160 assert_eq!(new.len(), 2)
1161 }
1162
1163 #[test]
1164 fn cast() {
1165 let a = get_chunked_array();
1166 let b = a.cast(&DataType::Int64).unwrap();
1167 assert_eq!(b.dtype(), &DataType::Int64)
1168 }
1169
1170 fn assert_slice_equal<T>(ca: &ChunkedArray<T>, eq: &[T::Native])
1171 where
1172 T: PolarsNumericType,
1173 {
1174 assert_eq!(ca.iter().map(|opt| opt.unwrap()).collect::<Vec<_>>(), eq)
1175 }
1176
1177 #[test]
1178 fn slice() {
1179 let mut first = UInt32Chunked::new(PlSmallStr::from_static("first"), &[0, 1, 2]);
1180 let second = UInt32Chunked::new(PlSmallStr::from_static("second"), &[3, 4, 5]);
1181 first.append(&second).unwrap();
1182 assert_slice_equal(&first.slice(0, 3), &[0, 1, 2]);
1183 assert_slice_equal(&first.slice(0, 4), &[0, 1, 2, 3]);
1184 assert_slice_equal(&first.slice(1, 4), &[1, 2, 3, 4]);
1185 assert_slice_equal(&first.slice(3, 2), &[3, 4]);
1186 assert_slice_equal(&first.slice(3, 3), &[3, 4, 5]);
1187 assert_slice_equal(&first.slice(-3, 3), &[3, 4, 5]);
1188 assert_slice_equal(&first.slice(-6, 6), &[0, 1, 2, 3, 4, 5]);
1189
1190 assert_eq!(first.slice(-7, 2).len(), 1);
1191 assert_eq!(first.slice(-3, 4).len(), 3);
1192 assert_eq!(first.slice(3, 4).len(), 3);
1193 assert_eq!(first.slice(10, 4).len(), 0);
1194 }
1195
1196 #[test]
1197 fn sorting() {
1198 let s = UInt32Chunked::new(PlSmallStr::EMPTY, &[9, 2, 4]);
1199 let sorted = s.sort(false);
1200 assert_slice_equal(&sorted, &[2, 4, 9]);
1201 let sorted = s.sort(true);
1202 assert_slice_equal(&sorted, &[9, 4, 2]);
1203
1204 let s: StringChunked = ["b", "a", "z"].iter().collect();
1205 let sorted = s.sort(false);
1206 assert_eq!(
1207 sorted.iter().collect::<Vec<_>>(),
1208 &[Some("a"), Some("b"), Some("z")]
1209 );
1210 let sorted = s.sort(true);
1211 assert_eq!(
1212 sorted.iter().collect::<Vec<_>>(),
1213 &[Some("z"), Some("b"), Some("a")]
1214 );
1215 let s: StringChunked = [Some("b"), None, Some("z")].iter().copied().collect();
1216 let sorted = s.sort(false);
1217 assert_eq!(
1218 sorted.iter().collect::<Vec<_>>(),
1219 &[None, Some("b"), Some("z")]
1220 );
1221 }
1222
1223 #[test]
1224 fn reverse() {
1225 let s = UInt32Chunked::new(PlSmallStr::EMPTY, &[1, 2, 3]);
1226 assert_slice_equal(&s.reverse(), &[3, 2, 1]);
1228 let s = UInt32Chunked::new(PlSmallStr::EMPTY, &[Some(1), None, Some(3)]);
1230 assert_eq!(Vec::from(&s.reverse()), &[Some(3), None, Some(1)]);
1231 let s = BooleanChunked::new(PlSmallStr::EMPTY, &[true, false]);
1232 assert_eq!(Vec::from(&s.reverse()), &[Some(false), Some(true)]);
1233
1234 let s = StringChunked::new(PlSmallStr::EMPTY, &["a", "b", "c"]);
1235 assert_eq!(Vec::from(&s.reverse()), &[Some("c"), Some("b"), Some("a")]);
1236
1237 let s = StringChunked::new(PlSmallStr::EMPTY, &[Some("a"), None, Some("c")]);
1238 assert_eq!(Vec::from(&s.reverse()), &[Some("c"), None, Some("a")]);
1239 }
1240
1241 #[test]
1242 #[cfg(feature = "dtype-categorical")]
1243 fn test_iter_categorical() {
1244 let ca = StringChunked::new(
1245 PlSmallStr::EMPTY,
1246 &[Some("foo"), None, Some("bar"), Some("ham")],
1247 );
1248 let cats = Categories::new(
1249 PlSmallStr::EMPTY,
1250 PlSmallStr::EMPTY,
1251 CategoricalPhysical::U32,
1252 );
1253 let ca = ca.cast(&DataType::from_categories(cats)).unwrap();
1254 let ca = ca.cat32().unwrap();
1255 let v: Vec<_> = ca.physical().iter().collect();
1256 assert_eq!(v, &[Some(0), None, Some(1), Some(2)]);
1257 }
1258
1259 #[test]
1260 #[ignore]
1261 fn test_shrink_to_fit() {
1262 let mut builder = StringChunkedBuilder::new(PlSmallStr::from_static("foo"), 2048);
1263 builder.append_value("foo");
1264 let mut arr = builder.finish();
1265 let before = arr
1266 .chunks()
1267 .iter()
1268 .map(|arr| arrow::compute::aggregate::estimated_bytes_size(arr.as_ref()))
1269 .sum::<usize>();
1270 arr.shrink_to_fit();
1271 let after = arr
1272 .chunks()
1273 .iter()
1274 .map(|arr| arrow::compute::aggregate::estimated_bytes_size(arr.as_ref()))
1275 .sum::<usize>();
1276 assert!(before > after);
1277 }
1278}