polars_core/chunked_array/array/
mod.rs1mod iterator;
4
5use std::borrow::Cow;
6
7use either::Either;
8
9use super::align_inner_chunks;
10use crate::prelude::*;
11
12impl ArrayChunked {
13 pub fn inner_dtype(&self) -> &DataType {
15 match self.dtype() {
16 DataType::Array(dt, _size) => dt.as_ref(),
17 _ => unreachable!(),
18 }
19 }
20
21 pub unsafe fn set_inner_dtype(&mut self, dtype: DataType) {
30 assert_eq!(dtype.to_physical(), self.inner_dtype().to_physical());
31 unsafe { self.to_logical(dtype) }
32 }
33
34 pub fn width(&self) -> usize {
35 match self.dtype() {
36 DataType::Array(_dt, size) => *size,
37 _ => unreachable!(),
38 }
39 }
40
41 pub unsafe fn to_logical(&mut self, inner_dtype: DataType) {
46 debug_assert_eq!(inner_dtype.to_physical(), self.inner_dtype().to_physical());
47 let width = self.width();
48 let fld = Arc::make_mut(&mut self.field);
49 fld.set_dtype(DataType::Array(Box::new(inner_dtype), width))
50 }
51
52 pub fn to_physical_repr(&self) -> Cow<'_, ArrayChunked> {
54 let Cow::Owned(physical_repr) = self.get_inner().to_physical_repr() else {
55 return Cow::Borrowed(self);
56 };
57
58 let chunk_len_validity_iter =
59 if physical_repr.chunks().len() == 1 && self.chunks().len() > 1 {
60 Either::Left(std::iter::once((self.len(), self.rechunk_validity())))
62 } else {
63 assert_eq!(self.chunks().len(), physical_repr.chunks().len());
65 Either::Right(
66 self.chunks()
67 .iter()
68 .map(|c| (c.len(), c.validity().cloned())),
69 )
70 };
71
72 let width = self.width();
73 let chunks: Vec<_> = chunk_len_validity_iter
74 .zip(physical_repr.into_chunks())
75 .map(|((len, validity), values)| {
76 FixedSizeListArray::new(
77 ArrowDataType::FixedSizeList(
78 Box::new(ArrowField::new(
79 LIST_VALUES_NAME,
80 values.dtype().clone(),
81 true,
82 )),
83 width,
84 ),
85 len,
86 values,
87 validity,
88 )
89 .to_boxed()
90 })
91 .collect();
92
93 let name = self.name().clone();
94 let dtype = DataType::Array(Box::new(self.inner_dtype().to_physical()), width);
95 Cow::Owned(unsafe { ArrayChunked::from_chunks_and_dtype_unchecked(name, chunks, dtype) })
96 }
97
98 pub unsafe fn from_physical_unchecked(&self, to_inner_dtype: DataType) -> PolarsResult<Self> {
104 debug_assert!(!self.inner_dtype().is_logical());
105
106 let chunks = self
107 .downcast_iter()
108 .map(|chunk| chunk.values())
109 .cloned()
110 .collect();
111
112 let inner = unsafe {
113 Series::from_chunks_and_dtype_unchecked(PlSmallStr::EMPTY, chunks, self.inner_dtype())
114 };
115 let inner = unsafe { inner.from_physical_unchecked(&to_inner_dtype) }?;
116
117 let chunks: Vec<_> = self
118 .downcast_iter()
119 .zip(inner.into_chunks())
120 .map(|(chunk, values)| {
121 FixedSizeListArray::new(
122 ArrowDataType::FixedSizeList(
123 Box::new(ArrowField::new(
124 LIST_VALUES_NAME,
125 values.dtype().clone(),
126 true,
127 )),
128 self.width(),
129 ),
130 chunk.len(),
131 values,
132 chunk.validity().cloned(),
133 )
134 .to_boxed()
135 })
136 .collect();
137
138 let name = self.name().clone();
139 let dtype = DataType::Array(Box::new(to_inner_dtype), self.width());
140 Ok(unsafe { Self::from_chunks_and_dtype_unchecked(name, chunks, dtype) })
141 }
142
143 pub fn get_inner(&self) -> Series {
145 let chunks: Vec<_> = self.downcast_iter().map(|c| c.values().clone()).collect();
146
147 unsafe {
149 Series::from_chunks_and_dtype_unchecked(self.name().clone(), chunks, self.inner_dtype())
150 }
151 }
152
153 pub fn inner_length(&self) -> usize {
156 self.downcast_iter().map(|c| c.values().len()).sum()
157 }
158
159 pub fn with_inner_values(&self, values: &Series) -> ArrayChunked {
164 if cfg!(debug_assertions) {
165 assert_eq!(values.len(), self.inner_length());
166 }
167
168 let values = align_inner_chunks(self.downcast_iter().map(|arr| arr.values().len()), values);
170 let values_dtype = values.dtype().clone();
171 let width = self.width();
172
173 let chunks = self
174 .downcast_iter()
175 .zip(values.into_chunks())
176 .map(|(ca_arr, v_arr)| {
177 debug_assert_eq!(ca_arr.values().len(), v_arr.len());
178 FixedSizeListArray::new(
179 FixedSizeListArray::default_datatype(v_arr.dtype().clone(), width),
180 ca_arr.len(),
181 v_arr,
182 ca_arr.validity().cloned(),
183 )
184 .to_boxed()
185 })
186 .collect::<Vec<_>>();
187
188 unsafe {
190 ArrayChunked::from_chunks_and_dtype_unchecked(
191 self.name().clone(),
192 chunks,
193 DataType::Array(Box::new(values_dtype), width),
194 )
195 }
196 }
197
198 pub fn apply_to_inner(
200 &self,
201 func: &dyn Fn(Series) -> PolarsResult<Series>,
202 ) -> PolarsResult<ArrayChunked> {
203 let ca = self.rechunk();
205 let arr = ca.downcast_as_array();
206
207 let elements = unsafe {
210 Series::from_chunks_and_dtype_unchecked(
211 self.name().clone(),
212 vec![arr.values().clone()],
213 ca.inner_dtype(),
214 )
215 };
216
217 let expected_len = elements.len();
218 let out: Series = func(elements)?;
219 polars_ensure!(
220 out.len() == expected_len,
221 ComputeError: "the function should apply element-wise, it removed elements instead"
222 );
223 let out = out.rechunk();
224 let values = out.chunks()[0].clone();
225
226 let inner_dtype = FixedSizeListArray::default_datatype(values.dtype().clone(), ca.width());
227 let arr = FixedSizeListArray::new(inner_dtype, arr.len(), values, arr.validity().cloned());
228
229 Ok(unsafe {
230 ArrayChunked::from_chunks_and_dtype_unchecked(
231 self.name().clone(),
232 vec![arr.into_boxed()],
233 DataType::Array(Box::new(out.dtype().clone()), self.width()),
234 )
235 })
236 }
237}