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