1use std::borrow::Cow;
2
3use polars_arrow::bitmap::Bitmap;
4use polars_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 unsafe { 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 mut leaf_array = self.propagate_nulls().unwrap_or_else(|| self.clone());
114 loop {
115 leaf_array = match leaf_array.dtype() {
116 DataType::Array(_, _) => leaf_array.array()?.get_inner(),
117 DataType::List(_) => leaf_array.explode(ExplodeOptions {
118 empty_as_null: false,
119 keep_nulls: false,
120 })?,
121 _ => break,
122 };
123 }
124 let leaf_array = leaf_array.rechunk();
125 let size = leaf_array.len();
126
127 let mut total_dim_size = 1;
128 let mut num_infers = 0;
129 for &dim in dimensions {
130 match dim {
131 ReshapeDimension::Infer => num_infers += 1,
132 ReshapeDimension::Specified(dim) => total_dim_size *= dim.get() as usize,
133 }
134 }
135
136 polars_ensure!(num_infers <= 1, InvalidOperation: "can only specify one inferred dimension");
137
138 if size == 0 {
139 polars_ensure!(
140 num_infers > 0 || total_dim_size == 0,
141 InvalidOperation: "cannot reshape empty array into shape without zero dimension: {}",
142 format_tuple!(dimensions),
143 );
144
145 let mut prev_arrow_dtype = leaf_array
146 .dtype()
147 .to_physical()
148 .to_arrow(CompatLevel::newest());
149 let mut prev_dtype = leaf_array.dtype().clone();
150 let mut prev_array = leaf_array.chunks()[0].clone();
151
152 let mut current_length = dimensions[0].get_or_infer(0);
154 let len_iter = dimensions[1..]
155 .iter()
156 .map(|d| {
157 let length = current_length as usize;
158 current_length *= d.get_or_infer(0);
159 length
160 })
161 .collect::<Vec<_>>();
162
163 for (dim, length) in dimensions[1..].iter().zip(len_iter).rev() {
165 let dim = dim.get_or_infer(0);
167 prev_arrow_dtype = prev_arrow_dtype.to_fixed_size_list(dim as usize, true);
168 prev_dtype = DataType::Array(Box::new(prev_dtype), dim as usize);
169
170 prev_array =
171 FixedSizeListArray::new(prev_arrow_dtype.clone(), length, prev_array, None)
172 .boxed();
173 }
174
175 return Ok(unsafe {
176 Series::from_chunks_and_dtype_unchecked(
177 leaf_array.name().clone(),
178 vec![prev_array],
179 &prev_dtype,
180 )
181 });
182 }
183
184 polars_ensure!(
185 total_dim_size > 0,
186 InvalidOperation: "cannot reshape non-empty array into shape containing a zero dimension: {}",
187 format_tuple!(dimensions)
188 );
189
190 polars_ensure!(
191 size.is_multiple_of(total_dim_size),
192 InvalidOperation: "cannot reshape array of size {} into shape {}", size, format_tuple!(dimensions)
193 );
194
195 let leaf_array = leaf_array.rechunk();
196 let mut prev_arrow_dtype = leaf_array
197 .dtype()
198 .to_physical()
199 .to_arrow(CompatLevel::newest());
200 let mut prev_dtype = leaf_array.dtype().clone();
201 let mut prev_array = leaf_array.chunks()[0].clone();
202 let inferred_size = (size / total_dim_size) as u64;
203 let outer_dimension = dimensions[0].get_or_infer(inferred_size);
204
205 for dim in dimensions[1..].iter().rev() {
207 let dim = dim.get_or_infer(inferred_size);
209 prev_arrow_dtype = prev_arrow_dtype.to_fixed_size_list(dim as usize, true);
210 prev_dtype = DataType::Array(Box::new(prev_dtype), dim as usize);
211
212 prev_array = FixedSizeListArray::new(
213 prev_arrow_dtype.clone(),
214 prev_array.len() / dim as usize,
215 prev_array,
216 None,
217 )
218 .boxed();
219 }
220
221 polars_ensure!(
222 prev_array.len() as u64 == outer_dimension,
223 InvalidOperation: "cannot reshape array of size {} into shape {}", size, format_tuple!(dimensions)
224 );
225
226 Ok(unsafe {
227 Series::from_chunks_and_dtype_unchecked(
228 leaf_array.name().clone(),
229 vec![prev_array],
230 &prev_dtype,
231 )
232 })
233 }
234
235 pub fn reshape_list(&self, dimensions: &[ReshapeDimension]) -> PolarsResult<Series> {
236 polars_ensure!(
237 !dimensions.is_empty(),
238 InvalidOperation: "at least one dimension must be specified"
239 );
240
241 let s = self;
242 let s = if let DataType::List(_) = s.dtype() {
243 Cow::Owned(s.explode(ExplodeOptions {
244 empty_as_null: false,
245 keep_nulls: true,
246 })?)
247 } else {
248 Cow::Borrowed(s)
249 };
250
251 let s_ref = s.as_ref();
252
253 match dimensions.len() {
256 1 => {
257 polars_ensure!(
258 dimensions[0].get().is_none_or( |dim| dim as usize == s_ref.len()),
259 InvalidOperation: "cannot reshape len {} into shape {:?}", s_ref.len(), dimensions,
260 );
261 Ok(s_ref.clone())
262 },
263 2 => {
264 let rows = dimensions[0];
265 let cols = dimensions[1];
266
267 if s_ref.is_empty() {
268 if rows.get_or_infer(0) == 0 && cols.get_or_infer(0) <= 1 {
269 return Ok(s_ref.to_unit_list().into_series());
270 } else {
271 polars_bail!(InvalidOperation: "cannot reshape len 0 into shape {}", format_tuple!(dimensions))
272 }
273 }
274
275 use ReshapeDimension as RD;
276 let (rows, cols) = match (rows, cols) {
279 (RD::Infer, RD::Specified(cols)) if cols.get() >= 1 => {
280 (s_ref.len() as u64 / cols.get(), cols.get())
281 },
282 (RD::Specified(rows), RD::Infer) if rows.get() >= 1 => {
283 (rows.get(), s_ref.len() as u64 / rows.get())
284 },
285 (RD::Infer, RD::Infer) => (s_ref.len() as u64, 1u64),
286 (RD::Specified(rows), RD::Specified(cols)) => (rows.get(), cols.get()),
287 _ => polars_bail!(InvalidOperation: "reshape of non-zero list into zero list"),
288 };
289
290 if rows as usize == s_ref.len() && cols == 1 {
292 return Ok(s_ref.to_unit_list().into_series());
293 }
294
295 polars_ensure!(
296 (rows*cols) as usize == s_ref.len() && rows >= 1 && cols >= 1,
297 InvalidOperation: "cannot reshape len {} into shape {:?}", s_ref.len(), dimensions,
298 );
299
300 let mut builder =
301 get_list_builder(s_ref.dtype(), s_ref.len(), rows as usize, s.name().clone());
302
303 let mut offset = 0u64;
304 for _ in 0..rows {
305 let row = s_ref.slice(offset as i64, cols as usize);
306 builder.append_series(&row).unwrap();
307 offset += cols;
308 }
309 Ok(builder.finish().into_series())
310 },
311 _ => {
312 polars_bail!(InvalidOperation: "more than two dimensions not supported in reshaping to List.\n\nConsider reshaping to Array type.");
313 },
314 }
315 }
316}
317
318#[cfg(test)]
319mod test {
320 use super::*;
321 use crate::prelude::*;
322
323 #[test]
324 fn test_to_list() -> PolarsResult<()> {
325 let s = Series::new("a".into(), &[1, 2, 3]);
326
327 let mut builder = get_list_builder(s.dtype(), s.len(), 1, s.name().clone());
328 builder.append_series(&s).unwrap();
329 let expected = builder.finish();
330
331 let out = s.implode()?;
332 assert!(expected.into_series().equals(&out.into_series()));
333
334 Ok(())
335 }
336
337 #[test]
338 fn test_reshape() -> PolarsResult<()> {
339 let s = Series::new("a".into(), &[1, 2, 3, 4]);
340
341 for (dims, list_len) in [
342 (&[-1, 1], 4),
343 (&[4, 1], 4),
344 (&[2, 2], 2),
345 (&[-1, 2], 2),
346 (&[2, -1], 2),
347 ] {
348 let dims = dims
349 .iter()
350 .map(|&v| ReshapeDimension::new(v))
351 .collect::<Vec<_>>();
352 let out = s.reshape_list(&dims)?;
353 assert_eq!(out.len(), list_len);
354 assert!(matches!(out.dtype(), DataType::List(_)));
355 assert_eq!(
356 out.explode(ExplodeOptions {
357 empty_as_null: true,
358 keep_nulls: true,
359 })?
360 .len(),
361 4
362 );
363 }
364
365 Ok(())
366 }
367}