Skip to content

Commit 45acc62

Browse files
authored
add more tests for window::shift and handle boundary cases (#386)
* add more doc test for window::shift * handle i64::MIN first * use Ok(make_array(array.data_ref().clone()))
1 parent 239558b commit 45acc62

File tree

1 file changed

+88
-22
lines changed

1 file changed

+88
-22
lines changed

arrow/src/compute/kernels/window.rs

Lines changed: 88 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717

1818
//! Defines windowing functions, like `shift`ing
1919
20-
use crate::{array::new_null_array, compute::concat};
21-
use num::{abs, clamp};
22-
2320
use crate::array::{Array, ArrayRef};
2421
use crate::{array::PrimitiveArray, datatypes::ArrowPrimitiveType, error::Result};
22+
use crate::{
23+
array::{make_array, new_null_array},
24+
compute::concat,
25+
};
26+
use num::{abs, clamp};
2527

2628
/// Shifts array by defined number of items (to left or right)
2729
/// A positive value for `offset` shifts the array to the right
@@ -33,56 +35,120 @@ use crate::{array::PrimitiveArray, datatypes::ArrowPrimitiveType, error::Result}
3335
/// use arrow::compute::shift;
3436
///
3537
/// let a: Int32Array = vec![Some(1), None, Some(4)].into();
38+
///
3639
/// // shift array 1 element to the right
3740
/// let res = shift(&a, 1).unwrap();
3841
/// let expected: Int32Array = vec![None, Some(1), None].into();
39-
/// assert_eq!(res.as_ref(), &expected)
42+
/// assert_eq!(res.as_ref(), &expected);
43+
///
44+
/// // shift array 1 element to the left
45+
/// let res = shift(&a, -1).unwrap();
46+
/// let expected: Int32Array = vec![None, Some(4), None].into();
47+
/// assert_eq!(res.as_ref(), &expected);
48+
///
49+
/// // shift array 0 element, although not recommended
50+
/// let res = shift(&a, 0).unwrap();
51+
/// let expected: Int32Array = vec![Some(1), None, Some(4)].into();
52+
/// assert_eq!(res.as_ref(), &expected);
53+
///
54+
/// // shift array 3 element tot he right
55+
/// let res = shift(&a, 3).unwrap();
56+
/// let expected: Int32Array = vec![None, None, None].into();
57+
/// assert_eq!(res.as_ref(), &expected);
4058
/// ```
4159
pub fn shift<T>(values: &PrimitiveArray<T>, offset: i64) -> Result<ArrayRef>
4260
where
4361
T: ArrowPrimitiveType,
4462
{
45-
// Compute slice
46-
let slice_offset = clamp(-offset, 0, values.len() as i64) as usize;
47-
let length = values.len() - abs(offset) as usize;
48-
let slice = values.slice(slice_offset, length);
49-
50-
// Generate array with remaining `null` items
51-
let nulls = abs(offset as i64) as usize;
63+
let value_len = values.len() as i64;
64+
if offset == 0 {
65+
Ok(make_array(values.data_ref().clone()))
66+
} else if offset == i64::MIN || abs(offset) >= value_len {
67+
Ok(new_null_array(&T::DATA_TYPE, values.len()))
68+
} else {
69+
let slice_offset = clamp(-offset, 0, value_len) as usize;
70+
let length = values.len() - abs(offset) as usize;
71+
let slice = values.slice(slice_offset, length);
5272

53-
let null_arr = new_null_array(&T::DATA_TYPE, nulls);
73+
// Generate array with remaining `null` items
74+
let nulls = abs(offset) as usize;
75+
let null_arr = new_null_array(&T::DATA_TYPE, nulls);
5476

55-
// Concatenate both arrays, add nulls after if shift > 0 else before
56-
if offset > 0 {
57-
concat(&[null_arr.as_ref(), slice.as_ref()])
58-
} else {
59-
concat(&[slice.as_ref(), null_arr.as_ref()])
77+
// Concatenate both arrays, add nulls after if shift > 0 else before
78+
if offset > 0 {
79+
concat(&[null_arr.as_ref(), slice.as_ref()])
80+
} else {
81+
concat(&[slice.as_ref(), null_arr.as_ref()])
82+
}
6083
}
6184
}
6285

6386
#[cfg(test)]
6487
mod tests {
65-
use crate::array::Int32Array;
66-
6788
use super::*;
89+
use crate::array::Int32Array;
6890

6991
#[test]
7092
fn test_shift_neg() {
7193
let a: Int32Array = vec![Some(1), None, Some(4)].into();
7294
let res = shift(&a, -1).unwrap();
73-
7495
let expected: Int32Array = vec![None, Some(4), None].into();
75-
7696
assert_eq!(res.as_ref(), &expected);
7797
}
7898

7999
#[test]
80100
fn test_shift_pos() {
81101
let a: Int32Array = vec![Some(1), None, Some(4)].into();
82102
let res = shift(&a, 1).unwrap();
83-
84103
let expected: Int32Array = vec![None, Some(1), None].into();
104+
assert_eq!(res.as_ref(), &expected);
105+
}
106+
107+
#[test]
108+
fn test_shift_nil() {
109+
let a: Int32Array = vec![Some(1), None, Some(4)].into();
110+
let res = shift(&a, 0).unwrap();
111+
let expected: Int32Array = vec![Some(1), None, Some(4)].into();
112+
assert_eq!(res.as_ref(), &expected);
113+
}
85114

115+
#[test]
116+
fn test_shift_boundary_pos() {
117+
let a: Int32Array = vec![Some(1), None, Some(4)].into();
118+
let res = shift(&a, 3).unwrap();
119+
let expected: Int32Array = vec![None, None, None].into();
120+
assert_eq!(res.as_ref(), &expected);
121+
}
122+
123+
#[test]
124+
fn test_shift_boundary_neg() {
125+
let a: Int32Array = vec![Some(1), None, Some(4)].into();
126+
let res = shift(&a, -3).unwrap();
127+
let expected: Int32Array = vec![None, None, None].into();
128+
assert_eq!(res.as_ref(), &expected);
129+
}
130+
131+
#[test]
132+
fn test_shift_boundary_neg_min() {
133+
let a: Int32Array = vec![Some(1), None, Some(4)].into();
134+
let res = shift(&a, i64::MIN).unwrap();
135+
let expected: Int32Array = vec![None, None, None].into();
136+
assert_eq!(res.as_ref(), &expected);
137+
}
138+
139+
#[test]
140+
fn test_shift_large_pos() {
141+
let a: Int32Array = vec![Some(1), None, Some(4)].into();
142+
let res = shift(&a, 1000).unwrap();
143+
let expected: Int32Array = vec![None, None, None].into();
144+
assert_eq!(res.as_ref(), &expected);
145+
}
146+
147+
#[test]
148+
fn test_shift_large_neg() {
149+
let a: Int32Array = vec![Some(1), None, Some(4)].into();
150+
let res = shift(&a, -1000).unwrap();
151+
let expected: Int32Array = vec![None, None, None].into();
86152
assert_eq!(res.as_ref(), &expected);
87153
}
88154
}

0 commit comments

Comments
 (0)