Path: blob/main/crates/polars-plan/src/plans/aexpr/function_expr/schema.rs
7889 views
use polars_core::utils::materialize_dyn_int;12use super::*;34impl IRFunctionExpr {5pub(crate) fn get_field(6&self,7_input_schema: &Schema,8fields: &[Field],9) -> PolarsResult<Field> {10use IRFunctionExpr::*;1112let mapper = FieldsMapper { fields };13match self {14// Namespaces15#[cfg(feature = "dtype-array")]16ArrayExpr(func) => func.get_field(mapper),17BinaryExpr(s) => s.get_field(mapper),18#[cfg(feature = "dtype-categorical")]19Categorical(func) => func.get_field(mapper),20#[cfg(feature = "dtype-extension")]21Extension(func) => func.get_field(mapper),22ListExpr(func) => func.get_field(mapper),23#[cfg(feature = "strings")]24StringExpr(s) => s.get_field(mapper),25#[cfg(feature = "dtype-struct")]26StructExpr(s) => s.get_field(mapper),27#[cfg(feature = "temporal")]28TemporalExpr(fun) => fun.get_field(mapper),29#[cfg(feature = "bitwise")]30Bitwise(fun) => fun.get_field(mapper),3132// Other expressions33Boolean(func) => func.get_field(mapper),34#[cfg(feature = "business")]35Business(func) => func.get_field(mapper),36#[cfg(feature = "abs")]37Abs => mapper.with_same_dtype(),38Negate => mapper.with_same_dtype(),39NullCount => mapper.with_dtype(IDX_DTYPE),40Pow(pow_function) => match pow_function {41IRPowFunction::Generic => mapper.pow_dtype(),42_ => mapper.map_numeric_to_float_dtype(true),43},44Coalesce => mapper.map_to_supertype(),45#[cfg(feature = "row_hash")]46Hash(..) => mapper.with_dtype(DataType::UInt64),47#[cfg(feature = "arg_where")]48ArgWhere => mapper.with_dtype(IDX_DTYPE),49#[cfg(feature = "index_of")]50IndexOf => mapper.with_dtype(IDX_DTYPE),51#[cfg(feature = "search_sorted")]52SearchSorted { .. } => mapper.with_dtype(IDX_DTYPE),53#[cfg(feature = "range")]54Range(func) => func.get_field(mapper),55#[cfg(feature = "trigonometry")]56Trigonometry(_) => mapper.map_to_float_dtype(),57#[cfg(feature = "trigonometry")]58Atan2 => mapper.map_to_float_dtype(),59#[cfg(feature = "sign")]60Sign => mapper61.ensure_satisfies(|_, dtype| dtype.is_numeric(), "sign")?62.with_same_dtype(),63FillNull => mapper.map_to_supertype(),64#[cfg(feature = "rolling_window")]65RollingExpr { function, options } => {66use IRRollingFunction::*;67match function {68Min | Max => mapper.with_same_dtype(),69Mean | Quantile | Std => mapper.moment_dtype(),70Var => mapper.var_dtype(),71Sum => mapper.sum_dtype(),72Rank => match options.fn_params {73Some(RollingFnParams::Rank {74method: RollingRankMethod::Average,75..76}) => mapper.with_dtype(DataType::Float64),77Some(RollingFnParams::Rank { .. }) => mapper.with_dtype(IDX_DTYPE),78_ => unreachable!("should be Some(RollingFnParams::Rank)"),79},80#[cfg(feature = "cov")]81CorrCov { .. } => mapper.map_to_float_dtype(),82#[cfg(feature = "moment")]83Skew | Kurtosis => mapper.map_to_float_dtype(),84Map(_) => mapper.try_map_field(|field| {85if options.weights.is_some() {86let dtype = match field.dtype() {87#[cfg(feature = "dtype-f16")]88DataType::Float16 => DataType::Float16,89DataType::Float32 => DataType::Float32,90_ => DataType::Float64,91};92Ok(Field::new(field.name().clone(), dtype))93} else {94Ok(field.clone())95}96}),97}98},99#[cfg(feature = "rolling_window_by")]100RollingExprBy {101function_by,102options,103..104} => {105use IRRollingFunctionBy::*;106match function_by {107MinBy | MaxBy => mapper.with_same_dtype(),108MeanBy | QuantileBy | StdBy => mapper.moment_dtype(),109VarBy => mapper.var_dtype(),110SumBy => mapper.sum_dtype(),111RankBy => match options.fn_params {112Some(RollingFnParams::Rank {113method: RollingRankMethod::Average,114..115}) => mapper.with_dtype(DataType::Float64),116Some(RollingFnParams::Rank { .. }) => mapper.with_dtype(IDX_DTYPE),117_ => unreachable!("should be Some(RollingFnParams::Rank)"),118},119}120},121Rechunk => mapper.with_same_dtype(),122Append { upcast } => {123if *upcast {124mapper.map_to_supertype()125} else {126mapper.with_same_dtype()127}128},129ShiftAndFill => mapper.with_same_dtype(),130DropNans => mapper.with_same_dtype(),131DropNulls => mapper.with_same_dtype(),132#[cfg(feature = "round_series")]133Clip {134has_min: _,135has_max: _,136} => mapper.with_same_dtype(),137#[cfg(feature = "mode")]138Mode { maintain_order: _ } => mapper.with_same_dtype(),139#[cfg(feature = "moment")]140Skew(_) => mapper.with_dtype(DataType::Float64),141#[cfg(feature = "moment")]142Kurtosis(..) => mapper.with_dtype(DataType::Float64),143ArgUnique | ArgMin | ArgMax | ArgSort { .. } => mapper.with_dtype(IDX_DTYPE),144Product => mapper.map_dtype(|dtype| {145use DataType as T;146match dtype {147#[cfg(feature = "dtype-f16")]148T::Float16 => T::Float16,149T::Float32 => T::Float32,150T::Float64 => T::Float64,151T::UInt64 => T::UInt64,152#[cfg(feature = "dtype-i128")]153T::Int128 => T::Int128,154_ => T::Int64,155}156}),157Repeat => mapper.with_same_dtype(),158#[cfg(feature = "rank")]159Rank { options, .. } => mapper.with_dtype(match options.method {160RankMethod::Average => DataType::Float64,161_ => IDX_DTYPE,162}),163#[cfg(feature = "dtype-struct")]164AsStruct => {165let mut field_names = PlHashSet::with_capacity(fields.len() - 1);166let struct_fields = fields167.iter()168.map(|f| {169polars_ensure!(170field_names.insert(f.name.as_str()),171duplicate_field = f.name()172);173Ok(f.clone())174})175.collect::<PolarsResult<Vec<_>>>()?;176Ok(Field::new(177fields[0].name().clone(),178DataType::Struct(struct_fields),179))180},181#[cfg(feature = "top_k")]182TopK { .. } => mapper.with_same_dtype(),183#[cfg(feature = "top_k")]184TopKBy { .. } => mapper.with_same_dtype(),185#[cfg(feature = "dtype-struct")]186ValueCounts {187sort: _,188parallel: _,189name,190normalize,191} => mapper.map_dtype(|dt| {192let count_dt = if *normalize {193DataType::Float64194} else {195IDX_DTYPE196};197DataType::Struct(vec![198Field::new(fields[0].name().clone(), dt.clone()),199Field::new(name.clone(), count_dt),200])201}),202#[cfg(feature = "unique_counts")]203UniqueCounts => mapper.with_dtype(IDX_DTYPE),204Shift | Reverse => mapper.with_same_dtype(),205#[cfg(feature = "cum_agg")]206CumCount { .. } => mapper.with_dtype(IDX_DTYPE),207#[cfg(feature = "cum_agg")]208CumSum { .. } => mapper.map_dtype(cum::dtypes::cum_sum),209#[cfg(feature = "cum_agg")]210CumProd { .. } => mapper.map_dtype(cum::dtypes::cum_prod),211#[cfg(feature = "cum_agg")]212CumMin { .. } => mapper.with_same_dtype(),213#[cfg(feature = "cum_agg")]214CumMax { .. } => mapper.with_same_dtype(),215#[cfg(feature = "approx_unique")]216ApproxNUnique => mapper.with_dtype(IDX_DTYPE),217#[cfg(feature = "hist")]218Hist {219include_category,220include_breakpoint,221..222} => {223if *include_breakpoint || *include_category {224let mut fields = Vec::with_capacity(3);225if *include_breakpoint {226fields.push(Field::new(227PlSmallStr::from_static("breakpoint"),228DataType::Float64,229));230}231if *include_category {232fields.push(Field::new(233PlSmallStr::from_static("category"),234DataType::from_categories(Categories::global()),235));236}237fields.push(Field::new(PlSmallStr::from_static("count"), IDX_DTYPE));238mapper.with_dtype(DataType::Struct(fields))239} else {240mapper.with_dtype(IDX_DTYPE)241}242},243#[cfg(feature = "diff")]244Diff(_) => mapper.map_dtype(|dt| match dt {245#[cfg(feature = "dtype-datetime")]246DataType::Datetime(tu, _) => DataType::Duration(*tu),247#[cfg(feature = "dtype-date")]248DataType::Date => DataType::Duration(TimeUnit::Microseconds),249#[cfg(feature = "dtype-time")]250DataType::Time => DataType::Duration(TimeUnit::Nanoseconds),251DataType::UInt64 | DataType::UInt32 => DataType::Int64,252DataType::UInt16 => DataType::Int32,253DataType::UInt8 => DataType::Int16,254dt => dt.clone(),255}),256#[cfg(feature = "pct_change")]257PctChange => mapper.map_dtype(|dt| match dt {258#[cfg(feature = "dtype-f16")]259DataType::Float16 => dt.clone(),260DataType::Float32 => dt.clone(),261_ => DataType::Float64,262}),263#[cfg(feature = "interpolate")]264Interpolate(method) => match method {265InterpolationMethod::Linear => mapper.map_numeric_to_float_dtype(false),266InterpolationMethod::Nearest => mapper.with_same_dtype(),267},268#[cfg(feature = "interpolate_by")]269InterpolateBy => mapper.map_numeric_to_float_dtype(true),270#[cfg(feature = "log")]271Entropy { .. } | Log1p | Exp => mapper.map_to_float_dtype(),272#[cfg(feature = "log")]273Log => mapper.log_dtype(),274Unique(_) => mapper.with_same_dtype(),275#[cfg(feature = "round_series")]276Round { .. } | RoundSF { .. } | Floor | Ceil => mapper.with_same_dtype(),277#[cfg(feature = "fused")]278Fused(_) => mapper.map_to_supertype(),279ConcatExpr(_) => mapper.map_to_supertype(),280#[cfg(feature = "cov")]281Correlation { .. } => mapper.map_to_float_dtype(),282#[cfg(feature = "peaks")]283PeakMin | PeakMax => mapper.with_dtype(DataType::Boolean),284#[cfg(feature = "cutqcut")]285Cut {286include_breaks: false,287..288} => mapper.with_dtype(DataType::from_categories(Categories::global())),289#[cfg(feature = "cutqcut")]290Cut {291include_breaks: true,292..293} => {294let struct_dt = DataType::Struct(vec![295Field::new(PlSmallStr::from_static("breakpoint"), DataType::Float64),296Field::new(297PlSmallStr::from_static("category"),298DataType::from_categories(Categories::global()),299),300]);301mapper.with_dtype(struct_dt)302},303#[cfg(feature = "repeat_by")]304RepeatBy => mapper.map_dtype(|dt| DataType::List(dt.clone().into())),305#[cfg(feature = "dtype-array")]306Reshape(dims) => mapper.try_map_dtype(|dt: &DataType| {307let mut wrapped_dtype = dt.leaf_dtype().clone();308for dim in dims[1..].iter().rev() {309let Some(array_size) = dim.get() else {310polars_bail!(InvalidOperation: "can only infer the first dimension");311};312wrapped_dtype = DataType::Array(Box::new(wrapped_dtype), array_size as usize);313}314Ok(wrapped_dtype)315}),316#[cfg(feature = "cutqcut")]317QCut {318include_breaks: false,319..320} => mapper.with_dtype(DataType::from_categories(Categories::global())),321#[cfg(feature = "cutqcut")]322QCut {323include_breaks: true,324..325} => {326let struct_dt = DataType::Struct(vec![327Field::new(PlSmallStr::from_static("breakpoint"), DataType::Float64),328Field::new(329PlSmallStr::from_static("category"),330DataType::from_categories(Categories::global()),331),332]);333mapper.with_dtype(struct_dt)334},335#[cfg(feature = "rle")]336RLE => mapper.map_dtype(|dt| {337DataType::Struct(vec![338Field::new(PlSmallStr::from_static("len"), IDX_DTYPE),339Field::new(PlSmallStr::from_static("value"), dt.clone()),340])341}),342#[cfg(feature = "rle")]343RLEID => mapper.with_dtype(IDX_DTYPE),344ToPhysical => mapper.to_physical_type(),345#[cfg(feature = "random")]346Random { .. } => mapper.with_same_dtype(),347SetSortedFlag(_) => mapper.with_same_dtype(),348#[cfg(feature = "ffi_plugin")]349FfiPlugin {350flags: _,351lib,352symbol,353kwargs,354} => unsafe { plugin::plugin_field(fields, lib, symbol.as_ref(), kwargs) },355356FoldHorizontal { return_dtype, .. } => match return_dtype {357None => mapper.with_same_dtype(),358Some(dtype) => mapper.with_dtype(dtype.clone()),359},360ReduceHorizontal { return_dtype, .. } => match return_dtype {361None => mapper.map_to_supertype(),362Some(dtype) => mapper.with_dtype(dtype.clone()),363},364#[cfg(feature = "dtype-struct")]365CumReduceHorizontal { return_dtype, .. } => match return_dtype {366None => mapper.with_dtype(DataType::Struct(fields.to_vec())),367Some(dtype) => mapper.with_dtype(DataType::Struct(368fields369.iter()370.map(|f| Field::new(f.name().clone(), dtype.clone()))371.collect(),372)),373},374#[cfg(feature = "dtype-struct")]375CumFoldHorizontal {376return_dtype,377include_init,378..379} => match return_dtype {380None => mapper.with_dtype(DataType::Struct(381fields382.iter()383.skip(usize::from(!include_init))384.map(|f| Field::new(f.name().clone(), fields[0].dtype().clone()))385.collect(),386)),387Some(dtype) => mapper.with_dtype(DataType::Struct(388fields389.iter()390.skip(usize::from(!include_init))391.map(|f| Field::new(f.name().clone(), dtype.clone()))392.collect(),393)),394},395396MaxHorizontal => mapper.map_to_supertype(),397MinHorizontal => mapper.map_to_supertype(),398SumHorizontal { .. } => mapper.map_to_supertype().map(|mut f| {399if f.dtype == DataType::Boolean {400f.dtype = IDX_DTYPE;401}402f403}),404MeanHorizontal { .. } => mapper.map_to_supertype().map(|mut f| {405match f.dtype {406#[cfg(feature = "dtype-f16")]407DataType::Float16 => {},408DataType::Float32 => {},409_ => {410f.dtype = DataType::Float64;411},412}413f414}),415#[cfg(feature = "ewma")]416EwmMean { .. } => mapper.map_numeric_to_float_dtype(true),417#[cfg(feature = "ewma_by")]418EwmMeanBy { .. } => mapper.map_numeric_to_float_dtype(true),419#[cfg(feature = "ewma")]420EwmStd { .. } => mapper.map_numeric_to_float_dtype(true),421#[cfg(feature = "ewma")]422EwmVar { .. } => mapper.var_dtype(),423#[cfg(feature = "replace")]424Replace => mapper.with_same_dtype(),425#[cfg(feature = "replace")]426ReplaceStrict { return_dtype } => mapper.replace_dtype(return_dtype.clone()),427FillNullWithStrategy(_) => mapper.with_same_dtype(),428GatherEvery { .. } => mapper.with_same_dtype(),429#[cfg(feature = "reinterpret")]430Reinterpret(signed) => {431let dt = if *signed {432DataType::Int64433} else {434DataType::UInt64435};436mapper.with_dtype(dt)437},438ExtendConstant => mapper.with_same_dtype(),439440RowEncode(..) => mapper.try_map_field(|_| {441Ok(Field::new(442PlSmallStr::from_static("row_encoded"),443DataType::BinaryOffset,444))445}),446#[cfg(feature = "dtype-struct")]447RowDecode(fields, _) => mapper.with_dtype(DataType::Struct(fields.to_vec())),448}449}450451pub(crate) fn output_name(&self) -> Option<OutputName> {452match self {453#[cfg(feature = "dtype-struct")]454IRFunctionExpr::StructExpr(IRStructFunction::FieldByName(name)) => {455Some(OutputName::Field(name.clone()))456},457_ => None,458}459}460}461462pub struct FieldsMapper<'a> {463fields: &'a [Field],464}465466impl<'a> FieldsMapper<'a> {467pub fn new(fields: &'a [Field]) -> Self {468Self { fields }469}470471pub fn args(&self) -> &[Field] {472self.fields473}474475/// Field with the same dtype.476pub fn with_same_dtype(&self) -> PolarsResult<Field> {477self.map_dtype(|dtype| dtype.clone())478}479480/// Set a dtype.481pub fn with_dtype(&self, dtype: DataType) -> PolarsResult<Field> {482Ok(Field::new(self.fields[0].name().clone(), dtype))483}484485/// Map a single dtype.486pub fn map_dtype(&self, func: impl FnOnce(&DataType) -> DataType) -> PolarsResult<Field> {487let dtype = func(self.fields[0].dtype());488Ok(Field::new(self.fields[0].name().clone(), dtype))489}490491pub fn get_fields_lens(&self) -> usize {492self.fields.len()493}494495/// Map a single field with a potentially failing mapper function.496pub fn try_map_field(497&self,498func: impl FnOnce(&Field) -> PolarsResult<Field>,499) -> PolarsResult<Field> {500func(&self.fields[0])501}502503pub fn var_dtype(&self) -> PolarsResult<Field> {504if self.fields[0].dtype().leaf_dtype().is_duration() {505let map_inner = |dt: &DataType| match dt {506dt if dt.is_temporal() => {507polars_bail!(InvalidOperation: "operation `var` is not supported for `{dt}`")508},509dt => Ok(dt.clone()),510};511512self.try_map_dtype(|dt| match dt {513#[cfg(feature = "dtype-array")]514DataType::Array(inner, _) => map_inner(inner),515DataType::List(inner) => map_inner(inner),516_ => map_inner(dt),517})518} else {519self.moment_dtype()520}521}522523pub fn moment_dtype(&self) -> PolarsResult<Field> {524let map_inner = |dt: &DataType| match dt {525DataType::Boolean => DataType::Float64,526#[cfg(feature = "dtype-f16")]527DataType::Float16 => DataType::Float16,528DataType::Float32 => DataType::Float32,529DataType::Float64 => DataType::Float64,530dt if dt.is_primitive_numeric() => DataType::Float64,531#[cfg(feature = "dtype-date")]532DataType::Date => DataType::Datetime(TimeUnit::Microseconds, None),533#[cfg(feature = "dtype-datetime")]534dt @ DataType::Datetime(_, _) => dt.clone(),535#[cfg(feature = "dtype-duration")]536dt @ DataType::Duration(_) => dt.clone(),537#[cfg(feature = "dtype-time")]538dt @ DataType::Time => dt.clone(),539#[cfg(feature = "dtype-decimal")]540DataType::Decimal(..) => DataType::Float64,541542// All other types get mapped to a single `null` of the same type.543dt => dt.clone(),544};545546self.map_dtype(|dt| match dt {547#[cfg(feature = "dtype-array")]548DataType::Array(inner, _) => map_inner(inner),549DataType::List(inner) => map_inner(inner),550_ => map_inner(dt),551})552}553554/// Map to a float supertype.555pub fn map_to_float_dtype(&self) -> PolarsResult<Field> {556self.map_dtype(|dtype| match dtype {557#[cfg(feature = "dtype-f16")]558DataType::Float16 => DataType::Float16,559DataType::Float32 => DataType::Float32,560_ => DataType::Float64,561})562}563564/// Map to a float supertype if numeric, else preserve565pub fn map_numeric_to_float_dtype(&self, coerce_decimal: bool) -> PolarsResult<Field> {566self.map_dtype(|dt| {567let should_coerce = match dt {568#[cfg(feature = "dtype-f16")]569DataType::Float16 => false,570DataType::Float32 => false,571#[cfg(feature = "dtype-decimal")]572DataType::Decimal(..) => coerce_decimal,573DataType::Boolean => true,574dt => dt.is_primitive_numeric(),575};576577if should_coerce {578DataType::Float64579} else {580dt.clone()581}582})583}584585/// Map to a physical type.586pub fn to_physical_type(&self) -> PolarsResult<Field> {587self.map_dtype(|dtype| dtype.to_physical())588}589590/// Map a single dtype with a potentially failing mapper function.591pub fn try_map_dtype(592&self,593func: impl FnOnce(&DataType) -> PolarsResult<DataType>,594) -> PolarsResult<Field> {595let dtype = func(self.fields[0].dtype())?;596Ok(Field::new(self.fields[0].name().clone(), dtype))597}598599/// Map all dtypes with a potentially failing mapper function.600pub fn try_map_dtypes(601&self,602func: impl FnOnce(&[&DataType]) -> PolarsResult<DataType>,603) -> PolarsResult<Field> {604let mut fld = self.fields[0].clone();605let dtypes = self606.fields607.iter()608.map(|fld| fld.dtype())609.collect::<Vec<_>>();610let new_type = func(&dtypes)?;611fld.coerce(new_type);612Ok(fld)613}614615/// Map the dtype to the "supertype" of all fields.616pub fn map_to_supertype(&self) -> PolarsResult<Field> {617let st = args_to_supertype(self.fields)?;618let mut first = self.fields[0].clone();619first.coerce(st);620Ok(first)621}622623/// Map the dtype to the dtype of the list/array elements.624pub fn map_to_list_and_array_inner_dtype(&self) -> PolarsResult<Field> {625let mut first = self.fields[0].clone();626let dt = first627.dtype()628.inner_dtype()629.cloned()630.unwrap_or_else(|| DataType::Unknown(Default::default()));631first.coerce(dt);632Ok(first)633}634635#[cfg(feature = "dtype-array")]636/// Map the dtype to the dtype of the array elements, with typo validation.637pub fn try_map_to_array_inner_dtype(&self) -> PolarsResult<Field> {638let dt = self.fields[0].dtype();639match dt {640DataType::Array(_, _) => self.map_to_list_and_array_inner_dtype(),641_ => polars_bail!(InvalidOperation: "expected Array type, got: {}", dt),642}643}644645/// Map the dtypes to the "supertype" of a list of lists.646pub fn map_to_list_supertype(&self) -> PolarsResult<Field> {647self.try_map_dtypes(|dts| {648let mut super_type_inner = None;649650for dt in dts {651match dt {652DataType::List(inner) => match super_type_inner {653None => super_type_inner = Some(*inner.clone()),654Some(st_inner) => {655super_type_inner = Some(try_get_supertype(&st_inner, inner)?)656},657},658dt => match super_type_inner {659None => super_type_inner = Some((*dt).clone()),660Some(st_inner) => {661super_type_inner = Some(try_get_supertype(&st_inner, dt)?)662},663},664}665}666Ok(DataType::List(Box::new(super_type_inner.unwrap())))667})668}669670/// Set the timezone of a datetime dtype.671#[cfg(feature = "timezones")]672pub fn map_datetime_dtype_timezone(&self, tz: Option<&TimeZone>) -> PolarsResult<Field> {673self.try_map_dtype(|dt| {674if let DataType::Datetime(tu, _) = dt {675Ok(DataType::Datetime(*tu, tz.cloned()))676} else {677polars_bail!(op = "replace-time-zone", got = dt, expected = "Datetime");678}679})680}681682pub fn sum_dtype(&self) -> PolarsResult<Field> {683use DataType::*;684self.map_dtype(|dtype| match dtype {685Int8 | UInt8 | Int16 | UInt16 => Int64,686Boolean => IDX_DTYPE,687dt => dt.clone(),688})689}690691pub fn nested_sum_type(&self) -> PolarsResult<Field> {692let mut first = self.fields[0].clone();693use DataType::*;694let dt = first.dtype().inner_dtype().cloned().ok_or_else(|| {695polars_err!(696InvalidOperation:"expected List or Array type, got dtype: {}",697first.dtype()698)699})?;700701match dt {702Boolean => first.coerce(IDX_DTYPE),703UInt8 | Int8 | Int16 | UInt16 => first.coerce(Int64),704_ => first.coerce(dt),705}706Ok(first)707}708709pub fn nested_mean_median_type(&self) -> PolarsResult<Field> {710let mut first = self.fields[0].clone();711use DataType::*;712let dt = first.dtype().inner_dtype().cloned().ok_or_else(|| {713polars_err!(714InvalidOperation:"expected List or Array type, got dtype: {}",715first.dtype()716)717})?;718719let new_dt = match dt {720#[cfg(feature = "dtype-datetime")]721Date => Datetime(TimeUnit::Microseconds, None),722dt if dt.is_temporal() => dt,723#[cfg(feature = "dtype-f16")]724Float16 => Float16,725Float32 => Float32,726_ => Float64,727};728first.coerce(new_dt);729Ok(first)730}731732pub(super) fn pow_dtype(&self) -> PolarsResult<Field> {733let dtype1 = self.fields[0].dtype();734let dtype2 = self.fields[1].dtype();735let out_dtype = if dtype1.is_integer() {736if dtype2.is_float() { dtype2 } else { dtype1 }737} else {738dtype1739};740Ok(Field::new(self.fields[0].name().clone(), out_dtype.clone()))741}742743pub(super) fn log_dtype(&self) -> PolarsResult<Field> {744let dtype1 = self.fields[0].dtype();745let dtype2 = self.fields[1].dtype();746let out_dtype = if dtype1.is_float() {747dtype1748} else if dtype2.is_float() {749dtype2750} else {751&DataType::Float64752};753Ok(Field::new(self.fields[0].name().clone(), out_dtype.clone()))754}755756#[cfg(feature = "extract_jsonpath")]757pub fn with_opt_dtype(&self, dtype: Option<DataType>) -> PolarsResult<Field> {758let dtype = dtype.unwrap_or_else(|| DataType::Unknown(Default::default()));759self.with_dtype(dtype)760}761762#[cfg(feature = "replace")]763pub fn replace_dtype(&self, return_dtype: Option<DataType>) -> PolarsResult<Field> {764let dtype = match return_dtype {765Some(dtype) => dtype,766None => {767let new = &self.fields[2];768let default = self.fields.get(3);769770// @HACK: Related to implicit implode see #22149.771let inner_dtype = new.dtype().inner_dtype().unwrap_or(new.dtype());772773match default {774Some(default) => try_get_supertype(default.dtype(), inner_dtype)?,775None => inner_dtype.clone(),776}777},778};779self.with_dtype(dtype)780}781782fn ensure_satisfies(783self,784mut f: impl FnMut(usize, &DataType) -> bool,785op: &'static str,786) -> PolarsResult<Self> {787for (i, field) in self.fields.iter().enumerate() {788polars_ensure!(789f(i, field.dtype()),790opidx = op,791idx = i,792self.fields[i].dtype()793);794}795796Ok(self)797}798}799800pub(crate) fn args_to_supertype<D: AsRef<DataType>>(dtypes: &[D]) -> PolarsResult<DataType> {801let mut st = dtypes[0].as_ref().clone();802for dt in &dtypes[1..] {803st = try_get_supertype(&st, dt.as_ref())?804}805806match (dtypes[0].as_ref(), &st) {807#[cfg(feature = "dtype-categorical")]808(cat @ DataType::Categorical(_, _), DataType::String) => st = cat.clone(),809_ => {810if let DataType::Unknown(kind) = st {811match kind {812UnknownKind::Float => st = DataType::Float64,813UnknownKind::Int(v) => {814st = materialize_dyn_int(v).dtype();815},816UnknownKind::Str => st = DataType::String,817_ => {},818}819}820},821}822823Ok(st)824}825826827