polars_core/utils/
series.rs1use std::rc::Rc;
2
3use polars_compute::find_validity_mismatch::find_validity_mismatch;
4use polars_compute::gather::take_unchecked;
5
6use crate::prelude::*;
7use crate::series::amortized_iter::AmortSeries;
8
9pub fn with_unstable_series<F, T>(dtype: &DataType, f: F) -> T
12where
13 F: Fn(&mut AmortSeries) -> T,
14{
15 let container = Series::full_null(PlSmallStr::EMPTY, 0, dtype);
16 let mut us = AmortSeries::new(Rc::new(container));
17
18 f(&mut us)
19}
20
21pub fn check_is_valid_struct_cast(
22 input_dtype: &DataType,
23 output_dtype: &DataType,
24 output_name: &PlSmallStr,
25) -> PolarsResult<()> {
26 use DataType as D;
27
28 let err = |msg: &str| -> PolarsError {
29 polars_err!(
30 InvalidOperation:
31 "cast from `{}` to `{}` failed in column '{}': {}\n\n\
32 Ensure that any output struct has the same number of fields as the input, and that all struct field names in the output are present in the input.\n\
33 Use `strict=False` to force the cast, and Polars will select the first n fields from the struct.",
34 input_dtype,
35 output_dtype,
36 output_name,
37 msg,
38 )
39 };
40
41 #[allow(clippy::single_match)]
42 match (input_dtype, output_dtype) {
43 #[cfg(feature = "dtype-struct")]
44 (D::Struct(l_fields), D::Struct(r_fields)) => {
45 if l_fields.len() != r_fields.len() {
46 return Err(err(&format!(
47 "structs do not have the same number of fields: {} vs {}",
48 l_fields.len(),
49 r_fields.len(),
50 )));
51 }
52 for (l, r) in Iterator::zip(l_fields.iter(), r_fields.iter()) {
53 if l.name() != r.name() {
54 return Err(err(&format!(
55 "structs field name mismatch: {} vs {}",
56 l.name(),
57 r.name()
58 )));
59 }
60 check_is_valid_struct_cast(l.dtype(), r.dtype(), output_name)?;
61 }
62 Ok(())
63 },
64 (D::List(input_dtype), D::List(output_dtype)) => {
65 check_is_valid_struct_cast(input_dtype, output_dtype, output_name)
66 },
67 #[cfg(feature = "dtype-array")]
68 (D::Array(input_dtype, _), D::Array(output_dtype, _)) => {
69 check_is_valid_struct_cast(input_dtype, output_dtype, output_name)
70 },
71 #[cfg(feature = "dtype-array")]
72 (D::List(input_dtype), D::Array(output_dtype, _))
73 | (D::Array(input_dtype, _), D::List(output_dtype)) => {
74 check_is_valid_struct_cast(input_dtype, output_dtype, output_name)
75 },
76 _ => Ok(()),
77 }
78}
79
80pub fn handle_casting_failures(input: &Series, output: &Series) -> PolarsResult<()> {
81 check_is_valid_struct_cast(input.dtype(), output.dtype(), output.name())?;
82
83 #[cfg(feature = "dtype-map")]
87 if output.dtype().contains_map() {
88 return Ok(());
89 }
90
91 let mut idxs = Vec::new();
92 input.find_validity_mismatch(output, &mut idxs);
93
94 if idxs.is_empty() {
95 return Ok(());
96 }
97
98 let num_failures = idxs.len();
99 let failures = input.take_slice(&idxs[..num_failures.min(10)])?;
100
101 let additional_info = match (input.dtype(), output.dtype()) {
102 (DataType::String, DataType::Date | DataType::Datetime(_, _)) => {
103 "\n\nYou might want to try:\n\
104 - setting `strict=False` to set values that cannot be converted to `null`\n\
105 - using `str.strptime`, `str.to_date`, or `str.to_datetime` and providing a format string"
106 },
107 #[cfg(feature = "dtype-categorical")]
108 (DataType::String, DataType::Enum(_, _)) => {
109 "\n\nEnsure that all values in the input column are present in the categories of the enum datatype."
110 },
111 _ if failures.len() < num_failures => {
112 "\n\nDid not show all failed cases as there were too many."
113 },
114 _ => "",
115 };
116
117 polars_bail!(
118 InvalidOperation:
119 "conversion from `{}` to `{}` failed in column '{}' for {} out of {} values: {}{}",
120 input.dtype(),
121 output.dtype(),
122 output.name(),
123 num_failures,
124 input.len(),
125 failures.fmt_list(),
126 additional_info,
127 )
128}
129
130pub fn handle_array_casting_failures(input: &dyn Array, output: &dyn Array) -> PolarsResult<()> {
131 let mut idxs = Vec::new();
132 find_validity_mismatch(input, output, &mut idxs);
133 if idxs.is_empty() {
134 return Ok(());
135 }
136
137 let num_failures = idxs.len();
138 let failures = PrimitiveArray::with_slice(&idxs[..num_failures.min(10)], |idxs| unsafe {
139 take_unchecked(input, &idxs)
140 });
141
142 polars_bail!(
143 InvalidOperation:
144 "conversion from `{}` to `{}` failed for {} out of {} values: {}",
145 DataType::from_arrow(input.dtype(), None),
146 DataType::from_arrow(output.dtype(), None),
147 num_failures,
148 input.len(),
149 Series::try_from((PlSmallStr::EMPTY, failures))?,
150 )
151}