Path: blob/main/crates/polars-expr/src/dispatch/range/utils.rs
7884 views
use polars_core::prelude::{1ChunkedArray, Column, Int64Chunked, IntoColumn, ListBuilderTrait, ListPrimitiveChunkedBuilder,2PolarsIntegerType, PolarsNumericType, PolarsResult, polars_bail, polars_ensure,3};45pub(super) fn temporal_series_to_i64_scalar(s: &Column) -> Option<i64> {6s.to_physical_repr().get(0).unwrap().extract::<i64>()7}8pub(super) fn ensure_items_contain_exactly_one_value(9values: &[&Column],10names: &[&str],11) -> PolarsResult<()> {12for (value, name) in values.iter().zip(names.iter()) {13polars_ensure!(14value.len() == 1,15ComputeError: "`{name}` must contain exactly one value, got {} values", value.len()16)17}18Ok(())19}2021/// Create a numeric ranges column from the given start/end/step columns and a range function.22pub(super) fn numeric_ranges_impl_broadcast<T, U, F>(23start: &ChunkedArray<T>,24end: &ChunkedArray<T>,25step: &Int64Chunked,26range_impl: F,27builder: &mut ListPrimitiveChunkedBuilder<U>,28) -> PolarsResult<Column>29where30T: PolarsIntegerType,31U: PolarsIntegerType,32F: Fn(T::Native, T::Native, i64, &mut ListPrimitiveChunkedBuilder<U>) -> PolarsResult<()>,33ListPrimitiveChunkedBuilder<U>: ListBuilderTrait,34{35match (start.len(), end.len(), step.len()) {36(len_start, len_end, len_step) if len_start == len_end && len_start == len_step => {37build_numeric_ranges::<_, _, _, T, U, F>(38start.downcast_iter().flatten(),39end.downcast_iter().flatten(),40step.downcast_iter().flatten(),41range_impl,42builder,43)?;44},45(1, len_end, 1) => {46let start_scalar = start.get(0);47let step_scalar = step.get(0);48match (start_scalar, step_scalar) {49(Some(start), Some(step)) => build_numeric_ranges::<_, _, _, T, U, F>(50std::iter::repeat(Some(&start)),51end.downcast_iter().flatten(),52std::iter::repeat(Some(&step)),53range_impl,54builder,55)?,56_ => build_nulls(builder, len_end),57}58},59(len_start, 1, 1) => {60let end_scalar = end.get(0);61let step_scalar = step.get(0);62match (end_scalar, step_scalar) {63(Some(end), Some(step)) => build_numeric_ranges::<_, _, _, T, U, F>(64start.downcast_iter().flatten(),65std::iter::repeat(Some(&end)),66std::iter::repeat(Some(&step)),67range_impl,68builder,69)?,70_ => build_nulls(builder, len_start),71}72},73(1, 1, len_step) => {74let start_scalar = start.get(0);75let end_scalar = end.get(0);76match (start_scalar, end_scalar) {77(Some(start), Some(end)) => build_numeric_ranges::<_, _, _, T, U, F>(78std::iter::repeat(Some(&start)),79std::iter::repeat(Some(&end)),80step.downcast_iter().flatten(),81range_impl,82builder,83)?,84_ => build_nulls(builder, len_step),85}86},87(len_start, len_end, 1) if len_start == len_end => {88let step_scalar = step.get(0);89match step_scalar {90Some(step) => build_numeric_ranges::<_, _, _, T, U, F>(91start.downcast_iter().flatten(),92end.downcast_iter().flatten(),93std::iter::repeat(Some(&step)),94range_impl,95builder,96)?,97None => build_nulls(builder, len_start),98}99},100(len_start, 1, len_step) if len_start == len_step => {101let end_scalar = end.get(0);102match end_scalar {103Some(end) => build_numeric_ranges::<_, _, _, T, U, F>(104start.downcast_iter().flatten(),105std::iter::repeat(Some(&end)),106step.downcast_iter().flatten(),107range_impl,108builder,109)?,110None => build_nulls(builder, len_start),111}112},113(1, len_end, len_step) if len_end == len_step => {114let start_scalar = start.get(0);115match start_scalar {116Some(start) => build_numeric_ranges::<_, _, _, T, U, F>(117std::iter::repeat(Some(&start)),118end.downcast_iter().flatten(),119step.downcast_iter().flatten(),120range_impl,121builder,122)?,123None => build_nulls(builder, len_end),124}125},126(len_start, len_end, len_step) => {127polars_bail!(128ComputeError:129"lengths of `start` ({}), `end` ({}) and `step` ({}) do not match",130len_start, len_end, len_step131)132},133};134let out = builder.finish().into_column();135Ok(out)136}137138/// Create a ranges column from the given start/end columns and a range function.139pub(super) fn temporal_ranges_impl_broadcast<T, U, F>(140start: &ChunkedArray<T>,141end: &ChunkedArray<T>,142range_impl: F,143builder: &mut ListPrimitiveChunkedBuilder<U>,144) -> PolarsResult<Column>145where146T: PolarsIntegerType,147U: PolarsIntegerType,148F: Fn(T::Native, T::Native, &mut ListPrimitiveChunkedBuilder<U>) -> PolarsResult<()>,149ListPrimitiveChunkedBuilder<U>: ListBuilderTrait,150{151match (start.len(), end.len()) {152(len_start, len_end) if len_start == len_end => {153build_temporal_ranges::<_, _, T, U, F>(154start.downcast_iter().flatten(),155end.downcast_iter().flatten(),156range_impl,157builder,158)?;159},160(1, len_end) => {161let start_scalar = start.get(0);162match start_scalar {163Some(start) => build_temporal_ranges::<_, _, T, U, F>(164std::iter::repeat(Some(&start)),165end.downcast_iter().flatten(),166range_impl,167builder,168)?,169None => build_nulls(builder, len_end),170}171},172(len_start, 1) => {173let end_scalar = end.get(0);174match end_scalar {175Some(end) => build_temporal_ranges::<_, _, T, U, F>(176start.downcast_iter().flatten(),177std::iter::repeat(Some(&end)),178range_impl,179builder,180)?,181None => build_nulls(builder, len_start),182}183},184(len_start, len_end) => {185polars_bail!(186ComputeError:187"lengths of `start` ({}) and `end` ({}) do not match",188len_start, len_end189)190},191};192let out = builder.finish().into_column();193Ok(out)194}195196/// Iterate over a start and end column and create a range with the step for each entry.197fn build_numeric_ranges<'a, I, J, K, T, U, F>(198start: I,199end: J,200step: K,201range_impl: F,202builder: &mut ListPrimitiveChunkedBuilder<U>,203) -> PolarsResult<()>204where205I: Iterator<Item = Option<&'a T::Native>>,206J: Iterator<Item = Option<&'a T::Native>>,207K: Iterator<Item = Option<&'a i64>>,208T: PolarsIntegerType,209U: PolarsIntegerType,210F: Fn(T::Native, T::Native, i64, &mut ListPrimitiveChunkedBuilder<U>) -> PolarsResult<()>,211ListPrimitiveChunkedBuilder<U>: ListBuilderTrait,212{213for ((start, end), step) in start.zip(end).zip(step) {214match (start, end, step) {215(Some(start), Some(end), Some(step)) => range_impl(*start, *end, *step, builder)?,216_ => builder.append_null(),217}218}219Ok(())220}221222/// Iterate over a start and end column and create a range for each entry.223fn build_temporal_ranges<'a, I, J, T, U, F>(224start: I,225end: J,226range_impl: F,227builder: &mut ListPrimitiveChunkedBuilder<U>,228) -> PolarsResult<()>229where230I: Iterator<Item = Option<&'a T::Native>>,231J: Iterator<Item = Option<&'a T::Native>>,232T: PolarsIntegerType,233U: PolarsIntegerType,234F: Fn(T::Native, T::Native, &mut ListPrimitiveChunkedBuilder<U>) -> PolarsResult<()>,235ListPrimitiveChunkedBuilder<U>: ListBuilderTrait,236{237for (start, end) in start.zip(end) {238match (start, end) {239(Some(start), Some(end)) => range_impl(*start, *end, builder)?,240_ => builder.append_null(),241}242}243Ok(())244}245246/// Add `n` nulls to the builder.247pub fn build_nulls<U>(builder: &mut ListPrimitiveChunkedBuilder<U>, n: usize)248where249U: PolarsNumericType,250ListPrimitiveChunkedBuilder<U>: ListBuilderTrait,251{252for _ in 0..n {253builder.append_null()254}255}256257258