Path: blob/main/crates/polars-core/src/series/any_value.rs
6940 views
use std::fmt::Write;12use arrow::bitmap::MutableBitmap;34#[cfg(feature = "dtype-categorical")]5use crate::chunked_array::builder::CategoricalChunkedBuilder;6use crate::chunked_array::builder::{AnonymousOwnedListBuilder, get_list_builder};7use crate::prelude::*;8use crate::utils::any_values_to_supertype;910impl<'a, T: AsRef<[AnyValue<'a>]>> NamedFrom<T, [AnyValue<'a>]> for Series {11/// Construct a new [`Series`] from a collection of [`AnyValue`].12///13/// # Panics14///15/// Panics if the values do not all share the same data type (with the exception16/// of [`DataType::Null`], which is always allowed).17///18/// [`AnyValue`]: crate::datatypes::AnyValue19fn new(name: PlSmallStr, values: T) -> Self {20let values = values.as_ref();21Series::from_any_values(name, values, true).expect("data types of values should match")22}23}2425impl Series {26/// Construct a new [`Series`] from a slice of AnyValues.27///28/// The data type of the resulting Series is determined by the `values`29/// and the `strict` parameter:30/// - If `strict` is `true`, the data type is equal to the data type of the31/// first non-null value. If any other non-null values do not match this32/// data type, an error is raised.33/// - If `strict` is `false`, the data type is the supertype of the `values`.34/// An error is returned if no supertype can be determined.35/// **WARNING**: A full pass over the values is required to determine the supertype.36/// - If no values were passed, the resulting data type is `Null`.37pub fn from_any_values(38name: PlSmallStr,39values: &[AnyValue],40strict: bool,41) -> PolarsResult<Self> {42fn get_first_non_null_dtype(values: &[AnyValue]) -> DataType {43let mut all_flat_null = true;44let first_non_null = values.iter().find(|av| {45if !av.is_null() {46all_flat_null = false47};48!av.is_nested_null()49});50match first_non_null {51Some(av) => av.dtype(),52None => {53if all_flat_null {54DataType::Null55} else {56// Second pass to check for the nested null value that57// toggled `all_flat_null` to false, e.g. a List(Null).58let first_nested_null = values.iter().find(|av| !av.is_null()).unwrap();59first_nested_null.dtype()60}61},62}63}64let dtype = if strict {65get_first_non_null_dtype(values)66} else {67// Currently does not work correctly for Decimal because equality is not implemented.68any_values_to_supertype(values)?69};7071// TODO: Remove this when Decimal data type equality is implemented.72#[cfg(feature = "dtype-decimal")]73if dtype.is_decimal() {74let dtype = DataType::Decimal(None, None);75return Self::from_any_values_and_dtype(name, values, &dtype, strict);76}7778Self::from_any_values_and_dtype(name, values, &dtype, strict)79}8081/// Construct a new [`Series`] with the given `dtype` from a slice of AnyValues.82///83/// If `strict` is `true`, an error is returned if the values do not match the given84/// data type. If `strict` is `false`, values that do not match the given data type85/// are cast. If casting is not possible, the values are set to null instead.86pub fn from_any_values_and_dtype(87name: PlSmallStr,88values: &[AnyValue],89dtype: &DataType,90strict: bool,91) -> PolarsResult<Self> {92if values.is_empty() {93return Ok(Self::new_empty(name, dtype));94}9596let mut s = match dtype {97#[cfg(feature = "dtype-i8")]98DataType::Int8 => any_values_to_integer::<Int8Type>(values, strict)?.into_series(),99#[cfg(feature = "dtype-i16")]100DataType::Int16 => any_values_to_integer::<Int16Type>(values, strict)?.into_series(),101DataType::Int32 => any_values_to_integer::<Int32Type>(values, strict)?.into_series(),102DataType::Int64 => any_values_to_integer::<Int64Type>(values, strict)?.into_series(),103#[cfg(feature = "dtype-i128")]104DataType::Int128 => any_values_to_integer::<Int128Type>(values, strict)?.into_series(),105#[cfg(feature = "dtype-u8")]106DataType::UInt8 => any_values_to_integer::<UInt8Type>(values, strict)?.into_series(),107#[cfg(feature = "dtype-u16")]108DataType::UInt16 => any_values_to_integer::<UInt16Type>(values, strict)?.into_series(),109DataType::UInt32 => any_values_to_integer::<UInt32Type>(values, strict)?.into_series(),110DataType::UInt64 => any_values_to_integer::<UInt64Type>(values, strict)?.into_series(),111DataType::Float32 => any_values_to_f32(values, strict)?.into_series(),112DataType::Float64 => any_values_to_f64(values, strict)?.into_series(),113DataType::Boolean => any_values_to_bool(values, strict)?.into_series(),114DataType::String => any_values_to_string(values, strict)?.into_series(),115DataType::Binary => any_values_to_binary(values, strict)?.into_series(),116DataType::BinaryOffset => any_values_to_binary_offset(values, strict)?.into_series(),117#[cfg(feature = "dtype-date")]118DataType::Date => any_values_to_date(values, strict)?.into_series(),119#[cfg(feature = "dtype-time")]120DataType::Time => any_values_to_time(values, strict)?.into_series(),121#[cfg(feature = "dtype-datetime")]122DataType::Datetime(tu, tz) => {123any_values_to_datetime(values, *tu, (*tz).clone(), strict)?.into_series()124},125#[cfg(feature = "dtype-duration")]126DataType::Duration(tu) => any_values_to_duration(values, *tu, strict)?.into_series(),127#[cfg(feature = "dtype-categorical")]128dt @ (DataType::Categorical(_, _) | DataType::Enum(_, _)) => {129any_values_to_categorical(values, dt, strict)?130},131#[cfg(feature = "dtype-decimal")]132DataType::Decimal(precision, scale) => {133any_values_to_decimal(values, *precision, *scale, strict)?.into_series()134},135DataType::List(inner) => any_values_to_list(values, inner, strict)?.into_series(),136#[cfg(feature = "dtype-array")]137DataType::Array(inner, size) => any_values_to_array(values, inner, strict, *size)?138.into_series()139.cast(&DataType::Array(inner.clone(), *size))?,140#[cfg(feature = "dtype-struct")]141DataType::Struct(fields) => any_values_to_struct(values, fields, strict)?,142#[cfg(feature = "object")]143DataType::Object(_) => any_values_to_object(values)?,144DataType::Null => Series::new_null(PlSmallStr::EMPTY, values.len()),145dt => {146polars_bail!(147InvalidOperation:148"constructing a Series with data type {dt:?} from AnyValues is not supported"149)150},151};152s.rename(name);153Ok(s)154}155}156157fn any_values_to_primitive_nonstrict<T: PolarsNumericType>(values: &[AnyValue]) -> ChunkedArray<T> {158values159.iter()160.map(|av| av.extract::<T::Native>())161.collect_trusted()162}163164fn any_values_to_integer<T: PolarsIntegerType>(165values: &[AnyValue],166strict: bool,167) -> PolarsResult<ChunkedArray<T>> {168fn any_values_to_integer_strict<T: PolarsIntegerType>(169values: &[AnyValue],170) -> PolarsResult<ChunkedArray<T>> {171let mut builder = PrimitiveChunkedBuilder::<T>::new(PlSmallStr::EMPTY, values.len());172for av in values {173match &av {174av if av.is_integer() => {175let opt_val = av.extract::<T::Native>();176let val = match opt_val {177Some(v) => v,178None => return Err(invalid_value_error(&T::get_static_dtype(), av)),179};180builder.append_value(val)181},182AnyValue::Null => builder.append_null(),183av => return Err(invalid_value_error(&T::get_static_dtype(), av)),184}185}186Ok(builder.finish())187}188189if strict {190any_values_to_integer_strict::<T>(values)191} else {192Ok(any_values_to_primitive_nonstrict::<T>(values))193}194}195196fn any_values_to_f32(values: &[AnyValue], strict: bool) -> PolarsResult<Float32Chunked> {197fn any_values_to_f32_strict(values: &[AnyValue]) -> PolarsResult<Float32Chunked> {198let mut builder =199PrimitiveChunkedBuilder::<Float32Type>::new(PlSmallStr::EMPTY, values.len());200for av in values {201match av {202AnyValue::Float32(i) => builder.append_value(*i),203AnyValue::Null => builder.append_null(),204av => return Err(invalid_value_error(&DataType::Float32, av)),205}206}207Ok(builder.finish())208}209if strict {210any_values_to_f32_strict(values)211} else {212Ok(any_values_to_primitive_nonstrict::<Float32Type>(values))213}214}215fn any_values_to_f64(values: &[AnyValue], strict: bool) -> PolarsResult<Float64Chunked> {216fn any_values_to_f64_strict(values: &[AnyValue]) -> PolarsResult<Float64Chunked> {217let mut builder =218PrimitiveChunkedBuilder::<Float64Type>::new(PlSmallStr::EMPTY, values.len());219for av in values {220match av {221AnyValue::Float64(i) => builder.append_value(*i),222AnyValue::Float32(i) => builder.append_value(*i as f64),223AnyValue::Null => builder.append_null(),224av => return Err(invalid_value_error(&DataType::Float64, av)),225}226}227Ok(builder.finish())228}229if strict {230any_values_to_f64_strict(values)231} else {232Ok(any_values_to_primitive_nonstrict::<Float64Type>(values))233}234}235236fn any_values_to_bool(values: &[AnyValue], strict: bool) -> PolarsResult<BooleanChunked> {237let mut builder = BooleanChunkedBuilder::new(PlSmallStr::EMPTY, values.len());238for av in values {239match av {240AnyValue::Boolean(b) => builder.append_value(*b),241AnyValue::Null => builder.append_null(),242av => {243if strict {244return Err(invalid_value_error(&DataType::Boolean, av));245}246match av.cast(&DataType::Boolean) {247AnyValue::Boolean(b) => builder.append_value(b),248_ => builder.append_null(),249}250},251}252}253Ok(builder.finish())254}255256fn any_values_to_string(values: &[AnyValue], strict: bool) -> PolarsResult<StringChunked> {257fn any_values_to_string_strict(values: &[AnyValue]) -> PolarsResult<StringChunked> {258let mut builder = StringChunkedBuilder::new(PlSmallStr::EMPTY, values.len());259for av in values {260match av {261AnyValue::String(s) => builder.append_value(s),262AnyValue::StringOwned(s) => builder.append_value(s),263AnyValue::Null => builder.append_null(),264av => return Err(invalid_value_error(&DataType::String, av)),265}266}267Ok(builder.finish())268}269fn any_values_to_string_nonstrict(values: &[AnyValue]) -> StringChunked {270let mut builder = StringChunkedBuilder::new(PlSmallStr::EMPTY, values.len());271let mut owned = String::new(); // Amortize allocations.272for av in values {273match av {274AnyValue::String(s) => builder.append_value(s),275AnyValue::StringOwned(s) => builder.append_value(s),276AnyValue::Null => builder.append_null(),277AnyValue::Binary(_) | AnyValue::BinaryOwned(_) => builder.append_null(),278av => {279owned.clear();280write!(owned, "{av}").unwrap();281builder.append_value(&owned);282},283}284}285builder.finish()286}287if strict {288any_values_to_string_strict(values)289} else {290Ok(any_values_to_string_nonstrict(values))291}292}293294fn any_values_to_binary(values: &[AnyValue], strict: bool) -> PolarsResult<BinaryChunked> {295fn any_values_to_binary_strict(values: &[AnyValue]) -> PolarsResult<BinaryChunked> {296let mut builder = BinaryChunkedBuilder::new(PlSmallStr::EMPTY, values.len());297for av in values {298match av {299AnyValue::Binary(s) => builder.append_value(*s),300AnyValue::BinaryOwned(s) => builder.append_value(&**s),301AnyValue::Null => builder.append_null(),302av => return Err(invalid_value_error(&DataType::Binary, av)),303}304}305Ok(builder.finish())306}307fn any_values_to_binary_nonstrict(values: &[AnyValue]) -> BinaryChunked {308values309.iter()310.map(|av| match av {311AnyValue::Binary(b) => Some(*b),312AnyValue::BinaryOwned(b) => Some(&**b),313AnyValue::String(s) => Some(s.as_bytes()),314AnyValue::StringOwned(s) => Some(s.as_str().as_bytes()),315_ => None,316})317.collect_trusted()318}319if strict {320any_values_to_binary_strict(values)321} else {322Ok(any_values_to_binary_nonstrict(values))323}324}325326fn any_values_to_binary_offset(327values: &[AnyValue],328strict: bool,329) -> PolarsResult<BinaryOffsetChunked> {330let mut builder = MutableBinaryArray::<i64>::new();331for av in values {332match av {333AnyValue::Binary(s) => builder.push(Some(*s)),334AnyValue::BinaryOwned(s) => builder.push(Some(&**s)),335AnyValue::Null => builder.push_null(),336av => {337if strict {338return Err(invalid_value_error(&DataType::Binary, av));339} else {340builder.push_null();341};342},343}344}345Ok(BinaryOffsetChunked::with_chunk(346Default::default(),347builder.into(),348))349}350351#[cfg(feature = "dtype-date")]352fn any_values_to_date(values: &[AnyValue], strict: bool) -> PolarsResult<DateChunked> {353let mut builder = PrimitiveChunkedBuilder::<Int32Type>::new(PlSmallStr::EMPTY, values.len());354for av in values {355match av {356AnyValue::Date(i) => builder.append_value(*i),357AnyValue::Null => builder.append_null(),358av => {359if strict {360return Err(invalid_value_error(&DataType::Date, av));361}362match av.cast(&DataType::Date) {363AnyValue::Date(i) => builder.append_value(i),364_ => builder.append_null(),365}366},367}368}369Ok(builder.finish().into_date())370}371372#[cfg(feature = "dtype-time")]373fn any_values_to_time(values: &[AnyValue], strict: bool) -> PolarsResult<TimeChunked> {374let mut builder = PrimitiveChunkedBuilder::<Int64Type>::new(PlSmallStr::EMPTY, values.len());375for av in values {376match av {377AnyValue::Time(i) => builder.append_value(*i),378AnyValue::Null => builder.append_null(),379av => {380if strict {381return Err(invalid_value_error(&DataType::Time, av));382}383match av.cast(&DataType::Time) {384AnyValue::Time(i) => builder.append_value(i),385_ => builder.append_null(),386}387},388}389}390Ok(builder.finish().into_time())391}392393#[cfg(feature = "dtype-datetime")]394fn any_values_to_datetime(395values: &[AnyValue],396time_unit: TimeUnit,397time_zone: Option<TimeZone>,398strict: bool,399) -> PolarsResult<DatetimeChunked> {400let mut builder = PrimitiveChunkedBuilder::<Int64Type>::new(PlSmallStr::EMPTY, values.len());401let target_dtype = DataType::Datetime(time_unit, time_zone.clone());402for av in values {403match av {404AnyValue::Datetime(i, tu, _) if *tu == time_unit => builder.append_value(*i),405AnyValue::DatetimeOwned(i, tu, _) if *tu == time_unit => builder.append_value(*i),406AnyValue::Null => builder.append_null(),407av => {408if strict {409return Err(invalid_value_error(&target_dtype, av));410}411match av.cast(&target_dtype) {412AnyValue::Datetime(i, _, _) => builder.append_value(i),413AnyValue::DatetimeOwned(i, _, _) => builder.append_value(i),414_ => builder.append_null(),415}416},417}418}419Ok(builder.finish().into_datetime(time_unit, time_zone))420}421422#[cfg(feature = "dtype-duration")]423fn any_values_to_duration(424values: &[AnyValue],425time_unit: TimeUnit,426strict: bool,427) -> PolarsResult<DurationChunked> {428let mut builder = PrimitiveChunkedBuilder::<Int64Type>::new(PlSmallStr::EMPTY, values.len());429let target_dtype = DataType::Duration(time_unit);430for av in values {431match av {432AnyValue::Duration(i, tu) if *tu == time_unit => builder.append_value(*i),433AnyValue::Null => builder.append_null(),434av => {435if strict {436return Err(invalid_value_error(&target_dtype, av));437}438match av.cast(&target_dtype) {439AnyValue::Duration(i, _) => builder.append_value(i),440_ => builder.append_null(),441}442},443}444}445Ok(builder.finish().into_duration(time_unit))446}447448#[cfg(feature = "dtype-categorical")]449fn any_values_to_categorical(450values: &[AnyValue],451dtype: &DataType,452strict: bool,453) -> PolarsResult<Series> {454with_match_categorical_physical_type!(dtype.cat_physical().unwrap(), |$C| {455let mut builder = CategoricalChunkedBuilder::<$C>::new(PlSmallStr::EMPTY, dtype.clone());456457let mut owned = String::new(); // Amortize allocations.458for av in values {459let ret = match av {460AnyValue::String(s) => builder.append_str(s),461AnyValue::StringOwned(s) => builder.append_str(s),462463&AnyValue::Enum(cat, &ref map) |464&AnyValue::EnumOwned(cat, ref map) |465&AnyValue::Categorical(cat, &ref map) |466&AnyValue::CategoricalOwned(cat, ref map) => builder.append_cat(cat, map),467468AnyValue::Binary(_) | AnyValue::BinaryOwned(_) if !strict => {469builder.append_null();470Ok(())471},472AnyValue::Null => {473builder.append_null();474Ok(())475}476477av => {478if strict {479return Err(invalid_value_error(&DataType::String, av));480}481482owned.clear();483write!(owned, "{av}").unwrap();484builder.append_str(&owned)485},486};487488if let Err(e) = ret {489if strict {490return Err(e);491} else {492builder.append_null();493}494}495}496497let ca = builder.finish();498Ok(ca.into_series())499})500}501502#[cfg(feature = "dtype-decimal")]503fn any_values_to_decimal(504values: &[AnyValue],505precision: Option<usize>,506scale: Option<usize>, // If None, we're inferring the scale.507strict: bool,508) -> PolarsResult<DecimalChunked> {509/// Get the maximum scale among AnyValues510fn infer_scale(511values: &[AnyValue],512precision: Option<usize>,513strict: bool,514) -> PolarsResult<usize> {515let mut max_scale = 0;516for av in values {517let av_scale = match av {518AnyValue::Decimal(_, scale) => *scale,519AnyValue::Null => continue,520av => {521if strict {522let target_dtype = DataType::Decimal(precision, None);523return Err(invalid_value_error(&target_dtype, av));524}525continue;526},527};528max_scale = max_scale.max(av_scale);529}530Ok(max_scale)531}532let scale = match scale {533Some(s) => s,534None => infer_scale(values, precision, strict)?,535};536let target_dtype = DataType::Decimal(precision, Some(scale));537538let mut builder = PrimitiveChunkedBuilder::<Int128Type>::new(PlSmallStr::EMPTY, values.len());539for av in values {540match av {541// Allow equal or less scale. We do want to support different scales even in 'strict' mode.542AnyValue::Decimal(v, s) if *s <= scale => {543if *s == scale {544builder.append_value(*v)545} else {546match av.strict_cast(&target_dtype) {547Some(AnyValue::Decimal(i, _)) => builder.append_value(i),548_ => builder.append_null(),549}550}551},552AnyValue::Null => builder.append_null(),553av => {554if strict {555return Err(invalid_value_error(&target_dtype, av));556}557// TODO: Precision check, else set to null558match av.strict_cast(&target_dtype) {559Some(AnyValue::Decimal(i, _)) => builder.append_value(i),560_ => builder.append_null(),561}562},563};564}565566// Build the array and do a precision check if needed.567builder.finish().into_decimal(precision, scale)568}569570fn any_values_to_list(571avs: &[AnyValue],572inner_type: &DataType,573strict: bool,574) -> PolarsResult<ListChunked> {575// GB:576// Lord forgive for the sins I have committed in this function. The amount of strange577// exceptions that need to happen for this to work are insane and I feel like I am going crazy.578//579// This function is essentially a copy of the `<ListChunked as FromIterator>` where it does not580// sample the datatype from the first element and instead we give it explicitly. This allows581// this function to properly assign a datatype if `avs` starts with a `null` value. Previously,582// this was solved by assigning the `dtype` again afterwards, but why? We should not link the583// implementation of these functions. We still need to assign the dtype of the ListArray and584// such, anyways.585//586// Then, `collect_ca_with_dtype` does not possess the necessary exceptions shown in this587// function to use that. I have tried adding the exceptions there and it broke other things. I588// really do feel like this is the simplest solution.589590let mut valid = true;591let capacity = avs.len();592593let ca = match inner_type {594// AnyValues with empty lists in python can create595// Series of an unknown dtype.596// We use the anonymousbuilder without a dtype597// the empty arrays is then not added (we add an extra offset instead)598// the next non-empty series then must have the correct dtype.599DataType::Null => {600let mut builder = AnonymousOwnedListBuilder::new(PlSmallStr::EMPTY, capacity, None);601for av in avs {602match av {603AnyValue::List(b) => builder.append_series(b)?,604AnyValue::Null => builder.append_null(),605_ => {606valid = false;607builder.append_null();608},609}610}611builder.finish()612},613614#[cfg(feature = "object")]615DataType::Object(_) => polars_bail!(nyi = "Nested object types"),616617_ => {618let mut builder =619get_list_builder(inner_type, capacity * 5, capacity, PlSmallStr::EMPTY);620for av in avs {621match av {622AnyValue::List(b) => match b.cast(inner_type) {623Ok(casted) => {624if casted.null_count() != b.null_count() {625valid = !strict;626}627builder.append_series(&casted)?;628},629Err(_) => {630valid = false;631for _ in 0..b.len() {632builder.append_null();633}634},635},636AnyValue::Null => builder.append_null(),637_ => {638valid = false;639builder.append_null()640},641}642}643644builder.finish()645},646};647648if strict && !valid {649polars_bail!(SchemaMismatch: "unexpected value while building Series of type {:?}", DataType::List(Box::new(inner_type.clone())));650}651652Ok(ca)653}654655#[cfg(feature = "dtype-array")]656fn any_values_to_array(657avs: &[AnyValue],658inner_type: &DataType,659strict: bool,660width: usize,661) -> PolarsResult<ArrayChunked> {662fn to_arr(s: &Series) -> Option<ArrayRef> {663if s.chunks().len() > 1 {664let s = s.rechunk();665Some(s.chunks()[0].clone())666} else {667Some(s.chunks()[0].clone())668}669}670671let target_dtype = DataType::Array(Box::new(inner_type.clone()), width);672673// This is handled downstream. The builder will choose the first non null type.674let mut valid = true;675#[allow(unused_mut)]676let mut out: ArrayChunked = if inner_type == &DataType::Null {677avs.iter()678.map(|av| match av {679AnyValue::List(b) | AnyValue::Array(b, _) => to_arr(b),680AnyValue::Null => None,681_ => {682valid = false;683None684},685})686.collect_ca_with_dtype(PlSmallStr::EMPTY, target_dtype.clone())687}688// Make sure that wrongly inferred AnyValues don't deviate from the datatype.689else {690avs.iter()691.map(|av| match av {692AnyValue::List(b) | AnyValue::Array(b, _) => {693if b.dtype() == inner_type {694to_arr(b)695} else {696let s = match b.cast(inner_type) {697Ok(out) => out,698Err(_) => Series::full_null(b.name().clone(), b.len(), inner_type),699};700to_arr(&s)701}702},703AnyValue::Null => None,704_ => {705valid = false;706None707},708})709.collect_ca_with_dtype(PlSmallStr::EMPTY, target_dtype.clone())710};711712if strict && !valid {713polars_bail!(SchemaMismatch: "unexpected value while building Series of type {:?}", target_dtype);714}715polars_ensure!(716out.width() == width,717SchemaMismatch: "got mixed size array widths where width {} was expected", width718);719720// Ensure the logical type is correct for nested types.721#[cfg(feature = "dtype-struct")]722if !matches!(inner_type, DataType::Null) && out.inner_dtype().is_nested() {723unsafe {724out.set_dtype(target_dtype);725};726}727728Ok(out)729}730731#[cfg(feature = "dtype-struct")]732fn _any_values_to_struct<'a>(733av_fields: &[Field],734av_values: &[AnyValue<'a>],735field_index: usize,736field: &Field,737fields: &[Field],738field_avs: &mut Vec<AnyValue<'a>>,739) {740// TODO: Optimize.741742let mut append_by_search = || {743// Search for the name.744if let Some(i) = av_fields745.iter()746.position(|av_fld| av_fld.name == field.name)747{748field_avs.push(av_values[i].clone());749return;750}751field_avs.push(AnyValue::Null)752};753754// All fields are available in this single value.755// We can use the index to get value.756if fields.len() == av_fields.len() {757if fields.iter().zip(av_fields.iter()).any(|(l, r)| l != r) {758append_by_search()759} else {760let av_val = av_values761.get(field_index)762.cloned()763.unwrap_or(AnyValue::Null);764field_avs.push(av_val)765}766}767// Not all fields are available, we search the proper field.768else {769// Search for the name.770append_by_search()771}772}773774#[cfg(feature = "dtype-struct")]775fn any_values_to_struct(776values: &[AnyValue],777fields: &[Field],778strict: bool,779) -> PolarsResult<Series> {780// Fast path for structs with no fields.781if fields.is_empty() {782return Ok(783StructChunked::from_series(PlSmallStr::EMPTY, values.len(), [].iter())?.into_series(),784);785}786787// The physical series fields of the struct.788let mut series_fields = Vec::with_capacity(fields.len());789let mut has_outer_validity = false;790let mut field_avs = Vec::with_capacity(values.len());791for (i, field) in fields.iter().enumerate() {792field_avs.clear();793794for av in values.iter() {795match av {796AnyValue::StructOwned(payload) => {797let av_fields = &payload.1;798let av_values = &payload.0;799_any_values_to_struct(av_fields, av_values, i, field, fields, &mut field_avs);800},801AnyValue::Struct(_, _, av_fields) => {802let av_values: Vec<_> = av._iter_struct_av().collect();803_any_values_to_struct(av_fields, &av_values, i, field, fields, &mut field_avs);804},805_ => {806has_outer_validity = true;807field_avs.push(AnyValue::Null)808},809}810}811// If the inferred dtype is null, we let auto inference work.812let s = if matches!(field.dtype, DataType::Null) {813Series::from_any_values(field.name().clone(), &field_avs, strict)?814} else {815Series::from_any_values_and_dtype(816field.name().clone(),817&field_avs,818&field.dtype,819strict,820)?821};822series_fields.push(s)823}824825let mut out =826StructChunked::from_series(PlSmallStr::EMPTY, values.len(), series_fields.iter())?;827if has_outer_validity {828let mut validity = MutableBitmap::new();829validity.extend_constant(values.len(), true);830for (i, v) in values.iter().enumerate() {831if matches!(v, AnyValue::Null) {832unsafe { validity.set_unchecked(i, false) }833}834}835out.set_outer_validity(Some(validity.freeze()))836}837Ok(out.into_series())838}839840#[cfg(feature = "object")]841fn any_values_to_object(values: &[AnyValue]) -> PolarsResult<Series> {842use crate::chunked_array::object::registry;843let converter = registry::get_object_converter();844let mut builder = registry::get_object_builder(PlSmallStr::EMPTY, values.len());845for av in values {846match av {847AnyValue::Object(val) => builder.append_value(val.as_any()),848AnyValue::Null => builder.append_null(),849_ => {850// This is needed because in Python users can send mixed types.851// This only works if you set a global converter.852let any = converter(av.as_borrowed());853builder.append_value(&*any)854},855}856}857858Ok(builder.to_series())859}860861fn invalid_value_error(dtype: &DataType, value: &AnyValue) -> PolarsError {862polars_err!(863SchemaMismatch:864"unexpected value while building Series of type {:?}; found value of type {:?}: {}",865dtype,866value.dtype(),867value868)869}870871872