1use std::borrow::Cow;
2
3use arrow::bitmap::Bitmap;
4use arrow::offset::{Offsets, OffsetsBuffer};
5use polars_compute::gather::sublist::list::array_to_unit_list;
6use polars_error::{PolarsResult, polars_bail, polars_ensure};
7use polars_utils::format_tuple;
8
9use crate::chunked_array::builder::get_list_builder;
10use crate::datatypes::{DataType, ListChunked};
11use crate::prelude::{IntoSeries, Series, *};
12
13impl Series {
14 pub fn get_leaf_array(&self) -> Series {
16 let s = self;
17 match s.dtype() {
18 #[cfg(feature = "dtype-array")]
19 DataType::Array(dtype, _) => {
20 let ca = s.array().unwrap();
21 let chunks = ca
22 .downcast_iter()
23 .map(|arr| arr.values().clone())
24 .collect::<Vec<_>>();
25 unsafe { Series::from_chunks_and_dtype_unchecked(s.name().clone(), chunks, dtype) }
27 .get_leaf_array()
28 },
29 DataType::List(dtype) => {
30 let ca = s.list().unwrap();
31 let chunks = ca
32 .downcast_iter()
33 .map(|arr| arr.values().clone())
34 .collect::<Vec<_>>();
35 unsafe { Series::from_chunks_and_dtype_unchecked(s.name().clone(), chunks, dtype) }
37 .get_leaf_array()
38 },
39 _ => s.clone(),
40 }
41 }
42
43 pub fn list_offsets_and_validities_recursive(
45 &self,
46 ) -> (Vec<OffsetsBuffer<i64>>, Vec<Option<Bitmap>>) {
47 let mut offsets = vec![];
48 let mut validities = vec![];
49
50 let mut s = self.rechunk();
51
52 while let DataType::List(_) = s.dtype() {
53 let ca = s.list().unwrap();
54 offsets.push(ca.offsets().unwrap());
55 validities.push(ca.rechunk_validity());
56 s = ca.get_inner();
57 }
58
59 (offsets, validities)
60 }
61
62 pub fn to_unit_list(&self) -> ListChunked {
65 let mut ca = ListChunked::from_chunk_iter(
66 self.name().clone(),
67 self.chunks()
68 .iter()
69 .map(|arr| array_to_unit_list(arr.clone())),
70 );
71 ca.set_inner_dtype(self.dtype().clone());
72 ca.set_fast_explode();
73 ca
74 }
75
76 pub fn implode(&self) -> PolarsResult<ListChunked> {
79 let s = self;
80 let s = s.rechunk();
81 let values = s.array_ref(0);
82
83 let offsets = vec![0i64, values.len() as i64];
84 let inner_type = s.dtype();
85
86 let dtype = ListArray::<i64>::default_datatype(values.dtype().clone());
87
88 let arr = unsafe {
90 ListArray::new(
91 dtype,
92 Offsets::new_unchecked(offsets).into(),
93 values.clone(),
94 None,
95 )
96 };
97
98 let mut ca = ListChunked::with_chunk(s.name().clone(), arr);
99 unsafe { ca.to_logical(inner_type.clone()) };
100 ca.set_fast_explode();
101 Ok(ca)
102 }
103
104 #[cfg(feature = "dtype-array")]
105 pub fn reshape_array(&self, dimensions: &[ReshapeDimension]) -> PolarsResult<Series> {
106 polars_ensure!(
107 !dimensions.is_empty(),
108 InvalidOperation: "at least one dimension must be specified"
109 );
110
111 let leaf_array = self
112 .trim_lists_to_normalized_offsets()
113 .as_ref()
114 .unwrap_or(self)
115 .get_leaf_array()
116 .rechunk();
117 let size = leaf_array.len();
118
119 let mut total_dim_size = 1;
120 let mut num_infers = 0;
121 for &dim in dimensions {
122 match dim {
123 ReshapeDimension::Infer => num_infers += 1,
124 ReshapeDimension::Specified(dim) => total_dim_size *= dim.get() as usize,
125 }
126 }
127
128 polars_ensure!(num_infers <= 1, InvalidOperation: "can only specify one inferred dimension");
129
130 if size == 0 {
131 polars_ensure!(
132 num_infers > 0 || total_dim_size == 0,
133 InvalidOperation: "cannot reshape empty array into shape without zero dimension: {}",
134 format_tuple!(dimensions),
135 );
136
137 let mut prev_arrow_dtype = leaf_array
138 .dtype()
139 .to_physical()
140 .to_arrow(CompatLevel::newest());
141 let mut prev_dtype = leaf_array.dtype().clone();
142 let mut prev_array = leaf_array.chunks()[0].clone();
143
144 let mut current_length = dimensions[0].get_or_infer(0);
146 let len_iter = dimensions[1..]
147 .iter()
148 .map(|d| {
149 let length = current_length as usize;
150 current_length *= d.get_or_infer(0);
151 length
152 })
153 .collect::<Vec<_>>();
154
155 for (dim, length) in dimensions[1..].iter().zip(len_iter).rev() {
157 let dim = dim.get_or_infer(0);
159 prev_arrow_dtype = prev_arrow_dtype.to_fixed_size_list(dim as usize, true);
160 prev_dtype = DataType::Array(Box::new(prev_dtype), dim as usize);
161
162 prev_array =
163 FixedSizeListArray::new(prev_arrow_dtype.clone(), length, prev_array, None)
164 .boxed();
165 }
166
167 return Ok(unsafe {
168 Series::from_chunks_and_dtype_unchecked(
169 leaf_array.name().clone(),
170 vec![prev_array],
171 &prev_dtype,
172 )
173 });
174 }
175
176 polars_ensure!(
177 total_dim_size > 0,
178 InvalidOperation: "cannot reshape non-empty array into shape containing a zero dimension: {}",
179 format_tuple!(dimensions)
180 );
181
182 polars_ensure!(
183 size.is_multiple_of(total_dim_size),
184 InvalidOperation: "cannot reshape array of size {} into shape {}", size, format_tuple!(dimensions)
185 );
186
187 let leaf_array = leaf_array.rechunk();
188 let mut prev_arrow_dtype = leaf_array
189 .dtype()
190 .to_physical()
191 .to_arrow(CompatLevel::newest());
192 let mut prev_dtype = leaf_array.dtype().clone();
193 let mut prev_array = leaf_array.chunks()[0].clone();
194 let inferred_size = (size / total_dim_size) as u64;
195 let outer_dimension = dimensions[0].get_or_infer(inferred_size);
196
197 for dim in dimensions[1..].iter().rev() {
199 let dim = dim.get_or_infer(inferred_size);
201 prev_arrow_dtype = prev_arrow_dtype.to_fixed_size_list(dim as usize, true);
202 prev_dtype = DataType::Array(Box::new(prev_dtype), dim as usize);
203
204 prev_array = FixedSizeListArray::new(
205 prev_arrow_dtype.clone(),
206 prev_array.len() / dim as usize,
207 prev_array,
208 None,
209 )
210 .boxed();
211 }
212
213 polars_ensure!(
214 prev_array.len() as u64 == outer_dimension,
215 InvalidOperation: "cannot reshape array of size {} into shape {}", size, format_tuple!(dimensions)
216 );
217
218 Ok(unsafe {
219 Series::from_chunks_and_dtype_unchecked(
220 leaf_array.name().clone(),
221 vec![prev_array],
222 &prev_dtype,
223 )
224 })
225 }
226
227 pub fn reshape_list(&self, dimensions: &[ReshapeDimension]) -> PolarsResult<Series> {
228 polars_ensure!(
229 !dimensions.is_empty(),
230 InvalidOperation: "at least one dimension must be specified"
231 );
232
233 let s = self;
234 let s = if let DataType::List(_) = s.dtype() {
235 Cow::Owned(s.explode(ExplodeOptions {
236 empty_as_null: false,
237 keep_nulls: true,
238 })?)
239 } else {
240 Cow::Borrowed(s)
241 };
242
243 let s_ref = s.as_ref();
244
245 match dimensions.len() {
248 1 => {
249 polars_ensure!(
250 dimensions[0].get().is_none_or( |dim| dim as usize == s_ref.len()),
251 InvalidOperation: "cannot reshape len {} into shape {:?}", s_ref.len(), dimensions,
252 );
253 Ok(s_ref.clone())
254 },
255 2 => {
256 let rows = dimensions[0];
257 let cols = dimensions[1];
258
259 if s_ref.is_empty() {
260 if rows.get_or_infer(0) == 0 && cols.get_or_infer(0) <= 1 {
261 return Ok(s_ref.to_unit_list().into_series());
262 } else {
263 polars_bail!(InvalidOperation: "cannot reshape len 0 into shape {}", format_tuple!(dimensions))
264 }
265 }
266
267 use ReshapeDimension as RD;
268 let (rows, cols) = match (rows, cols) {
271 (RD::Infer, RD::Specified(cols)) if cols.get() >= 1 => {
272 (s_ref.len() as u64 / cols.get(), cols.get())
273 },
274 (RD::Specified(rows), RD::Infer) if rows.get() >= 1 => {
275 (rows.get(), s_ref.len() as u64 / rows.get())
276 },
277 (RD::Infer, RD::Infer) => (s_ref.len() as u64, 1u64),
278 (RD::Specified(rows), RD::Specified(cols)) => (rows.get(), cols.get()),
279 _ => polars_bail!(InvalidOperation: "reshape of non-zero list into zero list"),
280 };
281
282 if rows as usize == s_ref.len() && cols == 1 {
284 return Ok(s_ref.to_unit_list().into_series());
285 }
286
287 polars_ensure!(
288 (rows*cols) as usize == s_ref.len() && rows >= 1 && cols >= 1,
289 InvalidOperation: "cannot reshape len {} into shape {:?}", s_ref.len(), dimensions,
290 );
291
292 let mut builder =
293 get_list_builder(s_ref.dtype(), s_ref.len(), rows as usize, s.name().clone());
294
295 let mut offset = 0u64;
296 for _ in 0..rows {
297 let row = s_ref.slice(offset as i64, cols as usize);
298 builder.append_series(&row).unwrap();
299 offset += cols;
300 }
301 Ok(builder.finish().into_series())
302 },
303 _ => {
304 polars_bail!(InvalidOperation: "more than two dimensions not supported in reshaping to List.\n\nConsider reshaping to Array type.");
305 },
306 }
307 }
308}
309
310#[cfg(test)]
311mod test {
312 use super::*;
313 use crate::prelude::*;
314
315 #[test]
316 fn test_to_list() -> PolarsResult<()> {
317 let s = Series::new("a".into(), &[1, 2, 3]);
318
319 let mut builder = get_list_builder(s.dtype(), s.len(), 1, s.name().clone());
320 builder.append_series(&s).unwrap();
321 let expected = builder.finish();
322
323 let out = s.implode()?;
324 assert!(expected.into_series().equals(&out.into_series()));
325
326 Ok(())
327 }
328
329 #[test]
330 fn test_reshape() -> PolarsResult<()> {
331 let s = Series::new("a".into(), &[1, 2, 3, 4]);
332
333 for (dims, list_len) in [
334 (&[-1, 1], 4),
335 (&[4, 1], 4),
336 (&[2, 2], 2),
337 (&[-1, 2], 2),
338 (&[2, -1], 2),
339 ] {
340 let dims = dims
341 .iter()
342 .map(|&v| ReshapeDimension::new(v))
343 .collect::<Vec<_>>();
344 let out = s.reshape_list(&dims)?;
345 assert_eq!(out.len(), list_len);
346 assert!(matches!(out.dtype(), DataType::List(_)));
347 assert_eq!(
348 out.explode(ExplodeOptions {
349 empty_as_null: true,
350 keep_nulls: true,
351 })?
352 .len(),
353 4
354 );
355 }
356
357 Ok(())
358 }
359}