Path: blob/main/crates/polars-plan/src/plans/aexpr/function_expr/range.rs
7889 views
use std::fmt::{Display, Formatter};12use polars_core::prelude::*;3use polars_ops::series::ClosedInterval;4#[cfg(feature = "temporal")]5use polars_time::{ClosedWindow, Duration};67use super::{FunctionOptions, IRFunctionExpr};8#[cfg(any(feature = "dtype-date", feature = "dtype-datetime"))]9use crate::dsl::function_expr::DateRangeArgs;10use crate::plans::aexpr::function_expr::FieldsMapper;11use crate::prelude::FunctionFlags;1213#[cfg_attr(feature = "ir_serde", derive(serde::Serialize, serde::Deserialize))]14#[derive(Clone, PartialEq, Debug, Eq, Hash)]15pub enum IRRangeFunction {16IntRange {17step: i64,18dtype: DataType,19},20IntRanges {21dtype: DataType,22},23LinearSpace {24closed: ClosedInterval,25},26LinearSpaces {27closed: ClosedInterval,28array_width: Option<usize>,29},30#[cfg(feature = "dtype-date")]31DateRange {32interval: Option<Duration>,33closed: ClosedWindow,34arg_type: DateRangeArgs,35},36#[cfg(feature = "dtype-date")]37DateRanges {38interval: Option<Duration>,39closed: ClosedWindow,40arg_type: DateRangeArgs,41},42#[cfg(feature = "dtype-datetime")]43DatetimeRange {44interval: Option<Duration>,45closed: ClosedWindow,46time_unit: Option<TimeUnit>,47time_zone: Option<TimeZone>,48arg_type: DateRangeArgs,49},50#[cfg(feature = "dtype-datetime")]51DatetimeRanges {52interval: Option<Duration>,53closed: ClosedWindow,54time_unit: Option<TimeUnit>,55time_zone: Option<TimeZone>,56arg_type: DateRangeArgs,57},58#[cfg(feature = "dtype-time")]59TimeRange {60interval: Duration,61closed: ClosedWindow,62},63#[cfg(feature = "dtype-time")]64TimeRanges {65interval: Duration,66closed: ClosedWindow,67},68}6970fn map_linspace_dtype(mapper: &FieldsMapper) -> PolarsResult<DataType> {71let fields = mapper.args();72let start_dtype = fields[0].dtype();73let end_dtype = fields[1].dtype();74Ok(match (start_dtype, end_dtype) {75#[cfg(feature = "dtype-f16")]76(&DataType::Float16, &DataType::Float16) => DataType::Float16,77(&DataType::Float32, &DataType::Float32) => DataType::Float32,78// A linear space of a Date produces a sequence of Datetimes79(dt1, dt2) if dt1.is_temporal() && dt1 == dt2 => {80if dt1 == &DataType::Date {81DataType::Datetime(TimeUnit::Microseconds, None)82} else {83dt1.clone()84}85},86(dt1, dt2) if !dt1.is_primitive_numeric() || !dt2.is_primitive_numeric() => {87polars_bail!(ComputeError:88"'start' and 'end' have incompatible dtypes, got {:?} and {:?}",89dt1, dt290)91},92_ => DataType::Float64,93})94}9596impl IRRangeFunction {97pub(super) fn get_field(&self, mapper: FieldsMapper) -> PolarsResult<Field> {98use IRRangeFunction::*;99match self {100IntRange { dtype, .. } => mapper.with_dtype(dtype.clone()),101IntRanges { dtype } => mapper.with_dtype(DataType::List(Box::new(dtype.clone()))),102LinearSpace { .. } => mapper.with_dtype(map_linspace_dtype(&mapper)?),103LinearSpaces {104closed: _,105array_width,106} => {107let inner = Box::new(map_linspace_dtype(&mapper)?);108let dt = match array_width {109Some(width) => DataType::Array(inner, *width),110None => DataType::List(inner),111};112mapper.with_dtype(dt)113},114#[cfg(feature = "dtype-date")]115DateRange {116interval: _,117closed: _,118arg_type: _,119} => mapper.with_dtype(DataType::Date),120#[cfg(feature = "dtype-date")]121DateRanges {122interval: _,123closed: _,124arg_type: _,125} => mapper.with_dtype(DataType::List(Box::new(DataType::Date))),126#[cfg(feature = "dtype-datetime")]127DatetimeRange {128interval: _,129closed: _,130time_unit,131time_zone,132arg_type: _,133} => {134// Output dtype may change based on `interval`, `time_unit`, and `time_zone`.135let dtype =136mapper.map_to_datetime_range_dtype(time_unit.as_ref(), time_zone.as_ref())?;137mapper.with_dtype(dtype)138},139#[cfg(feature = "dtype-datetime")]140DatetimeRanges {141interval: _,142closed: _,143time_unit,144time_zone,145arg_type: _,146} => {147// output dtype may change based on `interval`, `time_unit`, and `time_zone`148let inner_dtype =149mapper.map_to_datetime_range_dtype(time_unit.as_ref(), time_zone.as_ref())?;150mapper.with_dtype(DataType::List(Box::new(inner_dtype)))151},152#[cfg(feature = "dtype-time")]153TimeRange { .. } => mapper.with_dtype(DataType::Time),154#[cfg(feature = "dtype-time")]155TimeRanges { .. } => mapper.with_dtype(DataType::List(Box::new(DataType::Time))),156}157}158159pub fn function_options(&self) -> FunctionOptions {160use IRRangeFunction as R;161match self {162R::IntRange { .. } => {163FunctionOptions::row_separable().with_flags(|f| f | FunctionFlags::ALLOW_RENAME)164},165R::LinearSpace { .. } => {166FunctionOptions::row_separable().with_flags(|f| f | FunctionFlags::ALLOW_RENAME)167},168#[cfg(feature = "dtype-date")]169R::DateRange { .. } => {170FunctionOptions::row_separable().with_flags(|f| f | FunctionFlags::ALLOW_RENAME)171},172#[cfg(feature = "dtype-date")]173R::DateRanges { .. } => {174FunctionOptions::elementwise().with_flags(|f| f | FunctionFlags::ALLOW_RENAME)175},176#[cfg(feature = "dtype-datetime")]177R::DatetimeRange { .. } => {178FunctionOptions::row_separable().with_flags(|f| f | FunctionFlags::ALLOW_RENAME)179},180#[cfg(feature = "dtype-datetime")]181R::DatetimeRanges { .. } => {182FunctionOptions::elementwise().with_flags(|f| f | FunctionFlags::ALLOW_RENAME)183},184#[cfg(feature = "dtype-time")]185R::TimeRange { .. } => {186FunctionOptions::row_separable().with_flags(|f| f | FunctionFlags::ALLOW_RENAME)187},188R::IntRanges { .. } => {189FunctionOptions::elementwise().with_flags(|f| f | FunctionFlags::ALLOW_RENAME)190},191R::LinearSpaces { .. } => {192FunctionOptions::elementwise().with_flags(|f| f | FunctionFlags::ALLOW_RENAME)193},194#[cfg(feature = "dtype-time")]195R::TimeRanges { .. } => {196FunctionOptions::elementwise().with_flags(|f| f | FunctionFlags::ALLOW_RENAME)197},198}199}200}201202impl Display for IRRangeFunction {203fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {204use IRRangeFunction::*;205let s = match self {206IntRange { .. } => "int_range",207IntRanges { .. } => "int_ranges",208LinearSpace { .. } => "linear_space",209LinearSpaces { .. } => "linear_spaces",210#[cfg(feature = "dtype-date")]211DateRange { .. } => "date_range",212#[cfg(feature = "dtype-date")]213DateRanges { .. } => "date_ranges",214#[cfg(feature = "dtype-datetime")]215DatetimeRange { .. } => "datetime_range",216#[cfg(feature = "dtype-datetime")]217DatetimeRanges { .. } => "datetime_ranges",218#[cfg(feature = "dtype-time")]219TimeRange { .. } => "time_range",220#[cfg(feature = "dtype-time")]221TimeRanges { .. } => "time_ranges",222};223write!(f, "{s}")224}225}226227impl From<IRRangeFunction> for IRFunctionExpr {228fn from(value: IRRangeFunction) -> Self {229Self::Range(value)230}231}232233impl FieldsMapper<'_> {234pub fn map_to_datetime_range_dtype(235&self,236time_unit: Option<&TimeUnit>,237time_zone: Option<&TimeZone>,238) -> PolarsResult<DataType> {239let data_dtype = self.map_to_supertype()?.dtype;240241let (data_tu, data_tz) = if let DataType::Datetime(tu, tz) = data_dtype {242(tu, tz)243} else {244(TimeUnit::Microseconds, None)245};246247let tu = match time_unit {248Some(tu) => *tu,249None => data_tu,250};251252let tz = time_zone.cloned().or(data_tz);253254Ok(DataType::Datetime(tu, tz))255}256}257258259