Path: blob/main/crates/polars-expr/src/dispatch/shift_and_fill.rs
7884 views
use polars_core::downcast_as_macro_arg_physical;1use polars_core::error::{PolarsResult, polars_bail, polars_ensure, polars_warn};2use polars_core::prelude::{3AnyValue, ChunkShiftFill, ChunkedArray, Column, DataType, FromData, IntoColumn,4PolarsNumericType,5};67fn shift_and_fill_numeric<T>(ca: &ChunkedArray<T>, n: i64, fill_value: AnyValue) -> ChunkedArray<T>8where9T: PolarsNumericType,10ChunkedArray<T>: ChunkShiftFill<T, Option<T::Native>>,11{12let fill_value = fill_value.extract::<T::Native>();13ca.shift_and_fill(n, fill_value)14}1516#[cfg(any(17feature = "object",18feature = "dtype-struct",19feature = "dtype-categorical"20))]21fn shift_and_fill_with_mask(s: &Column, n: i64, fill_value: &Column) -> PolarsResult<Column> {22use arrow::array::BooleanArray;23use arrow::bitmap::BitmapBuilder;24use polars_core::prelude::BooleanChunked;2526let mask: BooleanChunked = if n > 0 {27let len = s.len();28let mut bits = BitmapBuilder::with_capacity(s.len());29bits.extend_constant(n as usize, false);30bits.extend_constant(len.saturating_sub(n as usize), true);31let mask = BooleanArray::from_data_default(bits.freeze(), None);32mask.into()33} else {34let length = s.len() as i64;35// n is negative, so subtraction.36let tipping_point = std::cmp::max(length + n, 0);37let mut bits = BitmapBuilder::with_capacity(s.len());38bits.extend_constant(tipping_point as usize, true);39bits.extend_constant(-n as usize, false);40let mask = BooleanArray::from_data_default(bits.freeze(), None);41mask.into()42};43s.shift(n).zip_with_same_type(&mask, fill_value)44}4546pub(super) fn shift_and_fill(args: &[Column]) -> PolarsResult<Column> {47let s = &args[0];48let n_s = &args[1].cast(&DataType::Int64)?;49let n = n_s.i64()?;5051if let Some(n) = n.get(0) {52let logical = s.dtype();53let physical = s.to_physical_repr();54let fill_value_s = &args[2];55let fill_value = fill_value_s.get(0).unwrap();5657use DataType::*;58match logical {59Boolean => {60let ca = s.bool()?;61let fill_value = match fill_value {62AnyValue::Boolean(v) => Some(v),63AnyValue::Null => None,64v => polars_bail!(ComputeError: "fill value '{}' is not supported", v),65};66ca.shift_and_fill(n, fill_value).into_column().cast(logical)67},68String => {69let ca = s.str()?;70let fill_value = match fill_value {71AnyValue::String(v) => Some(v),72AnyValue::StringOwned(ref v) => Some(v.as_str()),73AnyValue::Null => None,74v => polars_bail!(ComputeError: "fill value '{}' is not supported", v),75};76ca.shift_and_fill(n, fill_value).into_column().cast(logical)77},78List(_) => {79let ca = s.list()?;80let fill_value = match fill_value {81AnyValue::List(v) => Some(v),82AnyValue::Null => None,83v => polars_bail!(ComputeError: "fill value '{}' is not supported", v),84};85unsafe {86ca.shift_and_fill(n, fill_value.as_ref())87.into_column()88.from_physical_unchecked(logical)89}90},91#[cfg(feature = "object")]92Object(_) => shift_and_fill_with_mask(s, n, fill_value_s),93#[cfg(feature = "dtype-struct")]94Struct(_) => shift_and_fill_with_mask(s, n, fill_value_s),95#[cfg(feature = "dtype-categorical")]96Categorical(_, _) | Enum(_, _) => shift_and_fill_with_mask(s, n, fill_value_s),97dt if dt.is_primitive_numeric() || dt.is_logical() => {98macro_rules! dispatch {99($ca:expr, $n:expr, $fill_value:expr) => {{ shift_and_fill_numeric($ca, $n, $fill_value).into_column() }};100}101let out = downcast_as_macro_arg_physical!(physical, dispatch, n, fill_value);102unsafe { out.from_physical_unchecked(logical) }103},104dt => polars_bail!(opq = shift_and_fill, dt),105}106} else {107polars_warn!(108Deprecation, // @2.0109"shift value 'n' is null, which currently returns a column of null values. This will become an error in the future.",110);111Ok(Column::full_null(s.name().clone(), s.len(), s.dtype()))112}113}114115pub(super) fn shift(args: &[Column]) -> PolarsResult<Column> {116let s = &args[0];117let n_s = &args[1];118polars_ensure!(119n_s.len() == 1,120ComputeError: "n must be a single value."121);122123let n_s = n_s.cast(&DataType::Int64)?;124let n = n_s.i64()?;125126match n.get(0) {127Some(n) => Ok(s.shift(n)),128None => {129polars_warn!(130Deprecation, // @2.0131"shift value 'n' is null, which currently returns a column of null values. This will become an error in the future.",132);133Ok(Column::full_null(s.name().clone(), s.len(), s.dtype()))134},135}136}137138139