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