1#[cfg(feature = "dtype-categorical")]
2use arrow::compute::concatenate::concatenate_unchecked;
3use arrow::datatypes::Metadata;
4#[cfg(any(
5 feature = "dtype-date",
6 feature = "dtype-datetime",
7 feature = "dtype-time",
8 feature = "dtype-duration"
9))]
10use arrow::temporal_conversions::*;
11use polars_compute::cast::cast_unchecked as cast;
12use polars_error::feature_gated;
13use polars_utils::itertools::Itertools;
14
15use crate::chunked_array::cast::{CastOptions, cast_chunks};
16#[cfg(feature = "object")]
17use crate::chunked_array::object::extension::polars_extension::PolarsExtension;
18#[cfg(feature = "object")]
19use crate::chunked_array::object::registry::get_object_builder;
20#[cfg(feature = "timezones")]
21use crate::chunked_array::temporal::parse_fixed_offset;
22#[cfg(feature = "timezones")]
23use crate::chunked_array::temporal::validate_time_zone;
24use crate::prelude::*;
25
26impl Series {
27 pub fn from_chunk_and_dtype(
28 name: PlSmallStr,
29 chunk: ArrayRef,
30 dtype: &DataType,
31 ) -> PolarsResult<Self> {
32 if &dtype.to_physical().to_arrow(CompatLevel::newest()) != chunk.dtype() {
33 polars_bail!(
34 InvalidOperation: "cannot create a series of type '{dtype}' of arrow chunk with type '{:?}'",
35 chunk.dtype()
36 );
37 }
38
39 let series = unsafe { Self::from_chunks_and_dtype_unchecked(name, vec![chunk], dtype) };
41 Ok(series)
42 }
43
44 pub unsafe fn from_chunks_and_dtype_unchecked(
52 name: PlSmallStr,
53 chunks: Vec<ArrayRef>,
54 dtype: &DataType,
55 ) -> Self {
56 use DataType::*;
57 match dtype {
58 #[cfg(feature = "dtype-i8")]
59 Int8 => Int8Chunked::from_chunks(name, chunks).into_series(),
60 #[cfg(feature = "dtype-i16")]
61 Int16 => Int16Chunked::from_chunks(name, chunks).into_series(),
62 Int32 => Int32Chunked::from_chunks(name, chunks).into_series(),
63 Int64 => Int64Chunked::from_chunks(name, chunks).into_series(),
64 #[cfg(feature = "dtype-u8")]
65 UInt8 => UInt8Chunked::from_chunks(name, chunks).into_series(),
66 #[cfg(feature = "dtype-u16")]
67 UInt16 => UInt16Chunked::from_chunks(name, chunks).into_series(),
68 UInt32 => UInt32Chunked::from_chunks(name, chunks).into_series(),
69 UInt64 => UInt64Chunked::from_chunks(name, chunks).into_series(),
70 #[cfg(feature = "dtype-i128")]
71 Int128 => Int128Chunked::from_chunks(name, chunks).into_series(),
72 #[cfg(feature = "dtype-date")]
73 Date => Int32Chunked::from_chunks(name, chunks)
74 .into_date()
75 .into_series(),
76 #[cfg(feature = "dtype-time")]
77 Time => Int64Chunked::from_chunks(name, chunks)
78 .into_time()
79 .into_series(),
80 #[cfg(feature = "dtype-duration")]
81 Duration(tu) => Int64Chunked::from_chunks(name, chunks)
82 .into_duration(*tu)
83 .into_series(),
84 #[cfg(feature = "dtype-datetime")]
85 Datetime(tu, tz) => Int64Chunked::from_chunks(name, chunks)
86 .into_datetime(*tu, tz.clone())
87 .into_series(),
88 #[cfg(feature = "dtype-decimal")]
89 Decimal(precision, scale) => Int128Chunked::from_chunks(name, chunks)
90 .into_decimal_unchecked(
91 *precision,
92 scale.unwrap_or_else(|| unreachable!("scale should be set")),
93 )
94 .into_series(),
95 #[cfg(feature = "dtype-array")]
96 Array(_, _) => {
97 ArrayChunked::from_chunks_and_dtype_unchecked(name, chunks, dtype.clone())
98 .into_series()
99 },
100 List(_) => ListChunked::from_chunks_and_dtype_unchecked(name, chunks, dtype.clone())
101 .into_series(),
102 String => StringChunked::from_chunks(name, chunks).into_series(),
103 Binary => BinaryChunked::from_chunks(name, chunks).into_series(),
104 #[cfg(feature = "dtype-categorical")]
105 dt @ (Categorical(rev_map, ordering) | Enum(rev_map, ordering)) => {
106 let cats = UInt32Chunked::from_chunks(name, chunks);
107 let rev_map = rev_map.clone().unwrap_or_else(|| {
108 assert!(cats.is_empty());
109 Arc::new(RevMapping::default())
110 });
111 let mut ca = CategoricalChunked::from_cats_and_rev_map_unchecked(
112 cats,
113 rev_map,
114 matches!(dt, Enum(_, _)),
115 *ordering,
116 );
117 ca.set_fast_unique(false);
118 ca.into_series()
119 },
120 Boolean => BooleanChunked::from_chunks(name, chunks).into_series(),
121 Float32 => Float32Chunked::from_chunks(name, chunks).into_series(),
122 Float64 => Float64Chunked::from_chunks(name, chunks).into_series(),
123 BinaryOffset => BinaryOffsetChunked::from_chunks(name, chunks).into_series(),
124 #[cfg(feature = "dtype-struct")]
125 Struct(_) => {
126 let mut ca =
127 StructChunked::from_chunks_and_dtype_unchecked(name, chunks, dtype.clone());
128 StructChunked::propagate_nulls_mut(&mut ca);
129 ca.into_series()
130 },
131 #[cfg(feature = "object")]
132 Object(_) => {
133 if let Some(arr) = chunks[0].as_any().downcast_ref::<FixedSizeBinaryArray>() {
134 assert_eq!(chunks.len(), 1);
135 {
140 let pe = PolarsExtension::new(arr.clone());
141 let s = pe.get_series(&name);
142 pe.take_and_forget();
143 s
144 }
145 } else {
146 unsafe { get_object_builder(name, 0).from_chunks(chunks) }
147 }
148 },
149 Null => new_null(name, &chunks),
150 Unknown(_) => {
151 panic!("dtype is unknown; consider supplying data-types for all operations")
152 },
153 #[allow(unreachable_patterns)]
154 _ => unreachable!(),
155 }
156 }
157
158 pub unsafe fn _try_from_arrow_unchecked(
161 name: PlSmallStr,
162 chunks: Vec<ArrayRef>,
163 dtype: &ArrowDataType,
164 ) -> PolarsResult<Self> {
165 Self::_try_from_arrow_unchecked_with_md(name, chunks, dtype, None)
166 }
167
168 pub unsafe fn _try_from_arrow_unchecked_with_md(
173 name: PlSmallStr,
174 chunks: Vec<ArrayRef>,
175 dtype: &ArrowDataType,
176 md: Option<&Metadata>,
177 ) -> PolarsResult<Self> {
178 match dtype {
179 ArrowDataType::Utf8View => Ok(StringChunked::from_chunks(name, chunks).into_series()),
180 ArrowDataType::Utf8 | ArrowDataType::LargeUtf8 => {
181 let chunks =
182 cast_chunks(&chunks, &DataType::String, CastOptions::NonStrict).unwrap();
183 Ok(StringChunked::from_chunks(name, chunks).into_series())
184 },
185 ArrowDataType::BinaryView => Ok(BinaryChunked::from_chunks(name, chunks).into_series()),
186 ArrowDataType::LargeBinary => {
187 if let Some(md) = md {
188 if md.maintain_type() {
189 return Ok(BinaryOffsetChunked::from_chunks(name, chunks).into_series());
190 }
191 }
192 let chunks =
193 cast_chunks(&chunks, &DataType::Binary, CastOptions::NonStrict).unwrap();
194 Ok(BinaryChunked::from_chunks(name, chunks).into_series())
195 },
196 ArrowDataType::Binary => {
197 let chunks =
198 cast_chunks(&chunks, &DataType::Binary, CastOptions::NonStrict).unwrap();
199 Ok(BinaryChunked::from_chunks(name, chunks).into_series())
200 },
201 ArrowDataType::List(_) | ArrowDataType::LargeList(_) => {
202 let (chunks, dtype) = to_physical_and_dtype(chunks, md);
203 unsafe {
204 Ok(
205 ListChunked::from_chunks_and_dtype_unchecked(name, chunks, dtype)
206 .into_series(),
207 )
208 }
209 },
210 #[cfg(feature = "dtype-array")]
211 ArrowDataType::FixedSizeList(_, _) => {
212 let (chunks, dtype) = to_physical_and_dtype(chunks, md);
213 unsafe {
214 Ok(
215 ArrayChunked::from_chunks_and_dtype_unchecked(name, chunks, dtype)
216 .into_series(),
217 )
218 }
219 },
220 ArrowDataType::Boolean => Ok(BooleanChunked::from_chunks(name, chunks).into_series()),
221 #[cfg(feature = "dtype-u8")]
222 ArrowDataType::UInt8 => Ok(UInt8Chunked::from_chunks(name, chunks).into_series()),
223 #[cfg(feature = "dtype-u16")]
224 ArrowDataType::UInt16 => Ok(UInt16Chunked::from_chunks(name, chunks).into_series()),
225 ArrowDataType::UInt32 => Ok(UInt32Chunked::from_chunks(name, chunks).into_series()),
226 ArrowDataType::UInt64 => Ok(UInt64Chunked::from_chunks(name, chunks).into_series()),
227 #[cfg(feature = "dtype-i8")]
228 ArrowDataType::Int8 => Ok(Int8Chunked::from_chunks(name, chunks).into_series()),
229 #[cfg(feature = "dtype-i16")]
230 ArrowDataType::Int16 => Ok(Int16Chunked::from_chunks(name, chunks).into_series()),
231 ArrowDataType::Int32 => Ok(Int32Chunked::from_chunks(name, chunks).into_series()),
232 ArrowDataType::Int64 => Ok(Int64Chunked::from_chunks(name, chunks).into_series()),
233 ArrowDataType::Int128 => feature_gated!(
234 "dtype-i128",
235 Ok(Int128Chunked::from_chunks(name, chunks).into_series())
236 ),
237 ArrowDataType::Float16 => {
238 let chunks =
239 cast_chunks(&chunks, &DataType::Float32, CastOptions::NonStrict).unwrap();
240 Ok(Float32Chunked::from_chunks(name, chunks).into_series())
241 },
242 ArrowDataType::Float32 => Ok(Float32Chunked::from_chunks(name, chunks).into_series()),
243 ArrowDataType::Float64 => Ok(Float64Chunked::from_chunks(name, chunks).into_series()),
244 #[cfg(feature = "dtype-date")]
245 ArrowDataType::Date32 => {
246 let chunks =
247 cast_chunks(&chunks, &DataType::Int32, CastOptions::Overflowing).unwrap();
248 Ok(Int32Chunked::from_chunks(name, chunks)
249 .into_date()
250 .into_series())
251 },
252 #[cfg(feature = "dtype-datetime")]
253 ArrowDataType::Date64 => {
254 let chunks =
255 cast_chunks(&chunks, &DataType::Int64, CastOptions::Overflowing).unwrap();
256 let ca = Int64Chunked::from_chunks(name, chunks);
257 Ok(ca.into_datetime(TimeUnit::Milliseconds, None).into_series())
258 },
259 #[cfg(feature = "dtype-datetime")]
260 ArrowDataType::Timestamp(tu, tz) => {
261 let canonical_tz = DataType::canonical_timezone(tz);
262 let tz = match canonical_tz.as_deref() {
263 #[cfg(feature = "timezones")]
264 Some(tz_str) => match validate_time_zone(tz_str) {
265 Ok(_) => canonical_tz,
266 Err(_) => Some(parse_fixed_offset(tz_str)?),
267 },
268 _ => canonical_tz,
269 };
270 let chunks =
271 cast_chunks(&chunks, &DataType::Int64, CastOptions::NonStrict).unwrap();
272 let s = Int64Chunked::from_chunks(name, chunks)
273 .into_datetime(tu.into(), tz)
274 .into_series();
275 Ok(match tu {
276 ArrowTimeUnit::Second => &s * MILLISECONDS,
277 ArrowTimeUnit::Millisecond => s,
278 ArrowTimeUnit::Microsecond => s,
279 ArrowTimeUnit::Nanosecond => s,
280 })
281 },
282 #[cfg(feature = "dtype-duration")]
283 ArrowDataType::Duration(tu) => {
284 let chunks =
285 cast_chunks(&chunks, &DataType::Int64, CastOptions::NonStrict).unwrap();
286 let s = Int64Chunked::from_chunks(name, chunks)
287 .into_duration(tu.into())
288 .into_series();
289 Ok(match tu {
290 ArrowTimeUnit::Second => &s * MILLISECONDS,
291 ArrowTimeUnit::Millisecond => s,
292 ArrowTimeUnit::Microsecond => s,
293 ArrowTimeUnit::Nanosecond => s,
294 })
295 },
296 #[cfg(feature = "dtype-time")]
297 ArrowDataType::Time64(tu) | ArrowDataType::Time32(tu) => {
298 let mut chunks = chunks;
299 if matches!(dtype, ArrowDataType::Time32(_)) {
300 chunks =
301 cast_chunks(&chunks, &DataType::Int32, CastOptions::NonStrict).unwrap();
302 }
303 let chunks =
304 cast_chunks(&chunks, &DataType::Int64, CastOptions::NonStrict).unwrap();
305 let s = Int64Chunked::from_chunks(name, chunks)
306 .into_time()
307 .into_series();
308 Ok(match tu {
309 ArrowTimeUnit::Second => &s * NANOSECONDS,
310 ArrowTimeUnit::Millisecond => &s * 1_000_000,
311 ArrowTimeUnit::Microsecond => &s * 1_000,
312 ArrowTimeUnit::Nanosecond => s,
313 })
314 },
315 ArrowDataType::Decimal(precision, scale)
316 | ArrowDataType::Decimal256(precision, scale) => {
317 feature_gated!("dtype-decimal", {
318 polars_ensure!(*scale <= *precision, InvalidOperation: "invalid decimal precision and scale (prec={precision}, scale={scale})");
319 polars_ensure!(*precision <= 38, InvalidOperation: "polars does not support decimals about 38 precision");
320
321 let mut chunks = chunks;
322 for chunk in chunks.iter_mut() {
324 *chunk = std::mem::take(
325 chunk
326 .as_any_mut()
327 .downcast_mut::<PrimitiveArray<i128>>()
328 .unwrap(),
329 )
330 .to(ArrowDataType::Int128)
331 .to_boxed();
332 }
333 let s = Int128Chunked::from_chunks(name, chunks)
334 .into_decimal_unchecked(Some(*precision), *scale)
335 .into_series();
336 Ok(s)
337 })
338 },
339 ArrowDataType::Null => Ok(new_null(name, &chunks)),
340 #[cfg(not(feature = "dtype-categorical"))]
341 ArrowDataType::Dictionary(_, _, _) => {
342 panic!("activate dtype-categorical to convert dictionary arrays")
343 },
344 #[cfg(feature = "dtype-categorical")]
345 ArrowDataType::Dictionary(key_type, value_type, _) => {
346 use arrow::datatypes::IntegerType;
347 let arr = if chunks.len() > 1 {
349 concatenate_unchecked(&chunks)?
350 } else {
351 chunks[0].clone()
352 };
353
354 if matches!(
356 value_type.as_ref(),
357 ArrowDataType::Utf8
358 | ArrowDataType::LargeUtf8
359 | ArrowDataType::Utf8View
360 | ArrowDataType::Null
361 ) {
362 macro_rules! unpack_keys_values {
363 ($dt:ty) => {{
364 let arr = arr.as_any().downcast_ref::<DictionaryArray<$dt>>().unwrap();
365 let keys = arr.keys();
366 let keys = cast(keys, &ArrowDataType::UInt32).unwrap();
367 let values = arr.values();
368 let values = cast(&**values, &ArrowDataType::Utf8View)?;
369 (keys, values)
370 }};
371 }
372
373 use IntegerType as I;
374 let (keys, values) = match key_type {
375 I::Int8 => unpack_keys_values!(i8),
376 I::UInt8 => unpack_keys_values!(u8),
377 I::Int16 => unpack_keys_values!(i16),
378 I::UInt16 => unpack_keys_values!(u16),
379 I::Int32 => unpack_keys_values!(i32),
380 I::UInt32 => unpack_keys_values!(u32),
381 I::Int64 => unpack_keys_values!(i64),
382 _ => polars_bail!(
383 ComputeError: "dictionaries with unsigned 64-bit keys are not supported"
384 ),
385 };
386
387 let keys = keys.as_any().downcast_ref::<PrimitiveArray<u32>>().unwrap();
388 let values = values.as_any().downcast_ref::<Utf8ViewArray>().unwrap();
389
390 let (keys, values) =
392 polars_compute::propagate_dictionary::propagate_dictionary_value_nulls(
393 keys, values,
394 );
395
396 let mut ordering = CategoricalOrdering::default();
397 if let Some(metadata) = md {
398 if metadata.is_enum() {
399 return Ok(CategoricalChunked::from_cats_and_rev_map_unchecked(
402 UInt32Chunked::with_chunk(name, keys),
403 Arc::new(RevMapping::build_local(values)),
404 true,
405 CategoricalOrdering::Physical, )
407 .into_series());
408 } else if let Some(o) = metadata.categorical() {
409 ordering = o;
410 }
411 }
412
413 return Ok(CategoricalChunked::from_keys_and_values(
414 name, &keys, &values, ordering,
415 )
416 .into_series());
417 }
418
419 macro_rules! unpack_keys_values {
420 ($dt:ty) => {{
421 let arr = arr.as_any().downcast_ref::<DictionaryArray<$dt>>().unwrap();
422 let keys = arr.keys();
423 let keys = polars_compute::cast::primitive_as_primitive::<
424 $dt,
425 <IdxType as PolarsNumericType>::Native,
426 >(keys, &IDX_DTYPE.to_arrow(CompatLevel::newest()));
427 (arr.values(), keys)
428 }};
429 }
430
431 use IntegerType as I;
432 let (values, keys) = match key_type {
433 I::Int8 => unpack_keys_values!(i8),
434 I::UInt8 => unpack_keys_values!(u8),
435 I::Int16 => unpack_keys_values!(i16),
436 I::UInt16 => unpack_keys_values!(u16),
437 I::Int32 => unpack_keys_values!(i32),
438 I::UInt32 => unpack_keys_values!(u32),
439 I::Int64 => unpack_keys_values!(i64),
440 _ => polars_bail!(
441 ComputeError: "dictionaries with unsigned 64-bit keys are not supported"
442 ),
443 };
444
445 let values = Series::_try_from_arrow_unchecked_with_md(
447 name,
448 vec![values.clone()],
449 values.dtype(),
450 None,
451 )?;
452 let values = values.take_unchecked(&IdxCa::from_chunks_and_dtype(
453 PlSmallStr::EMPTY,
454 vec![keys.to_boxed()],
455 IDX_DTYPE,
456 ));
457
458 Ok(values)
459 },
460 #[cfg(feature = "object")]
461 ArrowDataType::Extension(ext)
462 if ext.name == EXTENSION_NAME && ext.metadata.is_some() =>
463 {
464 assert_eq!(chunks.len(), 1);
465 let arr = chunks[0]
466 .as_any()
467 .downcast_ref::<FixedSizeBinaryArray>()
468 .unwrap();
469 let s = {
474 let pe = PolarsExtension::new(arr.clone());
475 let s = pe.get_series(&name);
476 pe.take_and_forget();
477 s
478 };
479 Ok(s)
480 },
481 #[cfg(feature = "dtype-struct")]
482 ArrowDataType::Struct(_) => {
483 let (chunks, dtype) = to_physical_and_dtype(chunks, md);
484
485 unsafe {
486 let mut ca =
487 StructChunked::from_chunks_and_dtype_unchecked(name, chunks, dtype);
488 StructChunked::propagate_nulls_mut(&mut ca);
489 Ok(ca.into_series())
490 }
491 },
492 ArrowDataType::FixedSizeBinary(_) => {
493 let chunks = cast_chunks(&chunks, &DataType::Binary, CastOptions::NonStrict)?;
494 Ok(BinaryChunked::from_chunks(name, chunks).into_series())
495 },
496 ArrowDataType::Map(_, _) => map_arrays_to_series(name, chunks),
497 dt => polars_bail!(ComputeError: "cannot create series from {:?}", dt),
498 }
499 }
500}
501
502fn map_arrays_to_series(name: PlSmallStr, chunks: Vec<ArrayRef>) -> PolarsResult<Series> {
503 let chunks = chunks
504 .iter()
505 .map(|arr| {
506 let arr = arr.as_any().downcast_ref::<MapArray>().unwrap();
508 let inner = arr.field().clone();
509
510 let dtype = ListArray::<i32>::default_datatype(inner.dtype().clone());
512 Box::new(ListArray::<i32>::new(
513 dtype,
514 arr.offsets().clone(),
515 inner,
516 arr.validity().cloned(),
517 )) as ArrayRef
518 })
519 .collect::<Vec<_>>();
520 Series::try_from((name, chunks))
521}
522
523fn convert<F: Fn(&dyn Array) -> ArrayRef>(arr: &[ArrayRef], f: F) -> Vec<ArrayRef> {
524 arr.iter().map(|arr| f(&**arr)).collect()
525}
526
527#[allow(clippy::only_used_in_recursion)]
529unsafe fn to_physical_and_dtype(
530 arrays: Vec<ArrayRef>,
531 md: Option<&Metadata>,
532) -> (Vec<ArrayRef>, DataType) {
533 match arrays[0].dtype() {
534 ArrowDataType::Utf8 | ArrowDataType::LargeUtf8 => {
535 let chunks = cast_chunks(&arrays, &DataType::String, CastOptions::NonStrict).unwrap();
536 (chunks, DataType::String)
537 },
538 ArrowDataType::Binary | ArrowDataType::LargeBinary | ArrowDataType::FixedSizeBinary(_) => {
539 let chunks = cast_chunks(&arrays, &DataType::Binary, CastOptions::NonStrict).unwrap();
540 (chunks, DataType::Binary)
541 },
542 #[allow(unused_variables)]
543 dt @ ArrowDataType::Dictionary(_, _, _) => {
544 feature_gated!("dtype-categorical", {
545 let s = unsafe {
546 let dt = dt.clone();
547 Series::_try_from_arrow_unchecked_with_md(PlSmallStr::EMPTY, arrays, &dt, md)
548 }
549 .unwrap();
550 (s.chunks().clone(), s.dtype().clone())
551 })
552 },
553 ArrowDataType::List(field) => {
554 let out = convert(&arrays, |arr| {
555 cast(arr, &ArrowDataType::LargeList(field.clone())).unwrap()
556 });
557 to_physical_and_dtype(out, md)
558 },
559 #[cfg(feature = "dtype-array")]
560 ArrowDataType::FixedSizeList(field, size) => {
561 let values = arrays
562 .iter()
563 .map(|arr| {
564 let arr = arr.as_any().downcast_ref::<FixedSizeListArray>().unwrap();
565 arr.values().clone()
566 })
567 .collect::<Vec<_>>();
568
569 let (converted_values, dtype) =
570 to_physical_and_dtype(values, field.metadata.as_deref());
571
572 let arrays = arrays
573 .iter()
574 .zip(converted_values)
575 .map(|(arr, values)| {
576 let arr = arr.as_any().downcast_ref::<FixedSizeListArray>().unwrap();
577
578 let dtype = FixedSizeListArray::default_datatype(values.dtype().clone(), *size);
579 Box::from(FixedSizeListArray::new(
580 dtype,
581 arr.len(),
582 values,
583 arr.validity().cloned(),
584 )) as ArrayRef
585 })
586 .collect();
587 (arrays, DataType::Array(Box::new(dtype), *size))
588 },
589 ArrowDataType::LargeList(field) => {
590 let values = arrays
591 .iter()
592 .map(|arr| {
593 let arr = arr.as_any().downcast_ref::<ListArray<i64>>().unwrap();
594 arr.values().clone()
595 })
596 .collect::<Vec<_>>();
597
598 let (converted_values, dtype) =
599 to_physical_and_dtype(values, field.metadata.as_deref());
600
601 let arrays = arrays
602 .iter()
603 .zip(converted_values)
604 .map(|(arr, values)| {
605 let arr = arr.as_any().downcast_ref::<ListArray<i64>>().unwrap();
606
607 let dtype = ListArray::<i64>::default_datatype(values.dtype().clone());
608 Box::from(ListArray::<i64>::new(
609 dtype,
610 arr.offsets().clone(),
611 values,
612 arr.validity().cloned(),
613 )) as ArrayRef
614 })
615 .collect();
616 (arrays, DataType::List(Box::new(dtype)))
617 },
618 ArrowDataType::Struct(_fields) => {
619 feature_gated!("dtype-struct", {
620 let mut pl_fields = None;
621 let arrays = arrays
622 .iter()
623 .map(|arr| {
624 let arr = arr.as_any().downcast_ref::<StructArray>().unwrap();
625 let (values, dtypes): (Vec<_>, Vec<_>) = arr
626 .values()
627 .iter()
628 .zip(_fields.iter())
629 .map(|(value, field)| {
630 let mut out = to_physical_and_dtype(
631 vec![value.clone()],
632 field.metadata.as_deref(),
633 );
634 (out.0.pop().unwrap(), out.1)
635 })
636 .unzip();
637
638 let arrow_fields = values
639 .iter()
640 .zip(_fields.iter())
641 .map(|(arr, field)| {
642 ArrowField::new(field.name.clone(), arr.dtype().clone(), true)
643 })
644 .collect();
645 let arrow_array = Box::new(StructArray::new(
646 ArrowDataType::Struct(arrow_fields),
647 arr.len(),
648 values,
649 arr.validity().cloned(),
650 )) as ArrayRef;
651
652 if pl_fields.is_none() {
653 pl_fields = Some(
654 _fields
655 .iter()
656 .zip(dtypes)
657 .map(|(field, dtype)| Field::new(field.name.clone(), dtype))
658 .collect_vec(),
659 )
660 }
661
662 arrow_array
663 })
664 .collect_vec();
665
666 (arrays, DataType::Struct(pl_fields.unwrap()))
667 })
668 },
669 dt @ (ArrowDataType::Duration(_)
671 | ArrowDataType::Time32(_)
672 | ArrowDataType::Time64(_)
673 | ArrowDataType::Timestamp(_, _)
674 | ArrowDataType::Date32
675 | ArrowDataType::Decimal(_, _)
676 | ArrowDataType::Date64) => {
677 let dt = dt.clone();
678 let mut s = Series::_try_from_arrow_unchecked(PlSmallStr::EMPTY, arrays, &dt).unwrap();
679 let dtype = s.dtype().clone();
680 (std::mem::take(s.chunks_mut()), dtype)
681 },
682 dt => {
683 let dtype = DataType::from_arrow(dt, true, md);
684 (arrays, dtype)
685 },
686 }
687}
688
689fn check_types(chunks: &[ArrayRef]) -> PolarsResult<ArrowDataType> {
690 let mut chunks_iter = chunks.iter();
691 let dtype: ArrowDataType = chunks_iter
692 .next()
693 .ok_or_else(|| polars_err!(NoData: "expected at least one array-ref"))?
694 .dtype()
695 .clone();
696
697 for chunk in chunks_iter {
698 if chunk.dtype() != &dtype {
699 polars_bail!(
700 ComputeError: "cannot create series from multiple arrays with different types"
701 );
702 }
703 }
704 Ok(dtype)
705}
706
707impl Series {
708 pub fn try_new<T>(
709 name: PlSmallStr,
710 data: T,
711 ) -> Result<Self, <(PlSmallStr, T) as TryInto<Self>>::Error>
712 where
713 (PlSmallStr, T): TryInto<Self>,
714 {
715 <(PlSmallStr, T) as TryInto<Self>>::try_into((name, data))
718 }
719}
720
721impl TryFrom<(PlSmallStr, Vec<ArrayRef>)> for Series {
722 type Error = PolarsError;
723
724 fn try_from(name_arr: (PlSmallStr, Vec<ArrayRef>)) -> PolarsResult<Self> {
725 let (name, chunks) = name_arr;
726
727 let dtype = check_types(&chunks)?;
728 unsafe { Series::_try_from_arrow_unchecked(name, chunks, &dtype) }
731 }
732}
733
734impl TryFrom<(PlSmallStr, ArrayRef)> for Series {
735 type Error = PolarsError;
736
737 fn try_from(name_arr: (PlSmallStr, ArrayRef)) -> PolarsResult<Self> {
738 let (name, arr) = name_arr;
739 Series::try_from((name, vec![arr]))
740 }
741}
742
743impl TryFrom<(&ArrowField, Vec<ArrayRef>)> for Series {
744 type Error = PolarsError;
745
746 fn try_from(field_arr: (&ArrowField, Vec<ArrayRef>)) -> PolarsResult<Self> {
747 let (field, chunks) = field_arr;
748
749 let dtype = check_types(&chunks)?;
750
751 unsafe {
754 Series::_try_from_arrow_unchecked_with_md(
755 field.name.clone(),
756 chunks,
757 &dtype,
758 field.metadata.as_deref(),
759 )
760 }
761 }
762}
763
764impl TryFrom<(&ArrowField, ArrayRef)> for Series {
765 type Error = PolarsError;
766
767 fn try_from(field_arr: (&ArrowField, ArrayRef)) -> PolarsResult<Self> {
768 let (field, arr) = field_arr;
769 Series::try_from((field, vec![arr]))
770 }
771}
772
773pub unsafe trait IntoSeries {
781 fn is_series() -> bool {
782 false
783 }
784
785 fn into_series(self) -> Series
786 where
787 Self: Sized;
788}
789
790impl<T> From<ChunkedArray<T>> for Series
791where
792 T: PolarsDataType,
793 ChunkedArray<T>: IntoSeries,
794{
795 fn from(ca: ChunkedArray<T>) -> Self {
796 ca.into_series()
797 }
798}
799
800#[cfg(feature = "dtype-date")]
801impl From<DateChunked> for Series {
802 fn from(a: DateChunked) -> Self {
803 a.into_series()
804 }
805}
806
807#[cfg(feature = "dtype-datetime")]
808impl From<DatetimeChunked> for Series {
809 fn from(a: DatetimeChunked) -> Self {
810 a.into_series()
811 }
812}
813
814#[cfg(feature = "dtype-duration")]
815impl From<DurationChunked> for Series {
816 fn from(a: DurationChunked) -> Self {
817 a.into_series()
818 }
819}
820
821#[cfg(feature = "dtype-time")]
822impl From<TimeChunked> for Series {
823 fn from(a: TimeChunked) -> Self {
824 a.into_series()
825 }
826}
827
828unsafe impl IntoSeries for Arc<dyn SeriesTrait> {
829 fn into_series(self) -> Series {
830 Series(self)
831 }
832}
833
834unsafe impl IntoSeries for Series {
835 fn is_series() -> bool {
836 true
837 }
838
839 fn into_series(self) -> Series {
840 self
841 }
842}
843
844fn new_null(name: PlSmallStr, chunks: &[ArrayRef]) -> Series {
845 let len = chunks.iter().map(|arr| arr.len()).sum();
846 Series::new_null(name, len)
847}