Skip to content

Commit 1acb431

Browse files
authored
Merge pull request #306 from Robbepop/master
Added basic implementation of windows iterators
2 parents 4a40ac0 + 3abd2eb commit 1acb431

File tree

6 files changed

+175
-1
lines changed

6 files changed

+175
-1
lines changed

src/impl_methods.rs

+14
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use iterators::{
2727
new_inners_mut,
2828
exact_chunks_of,
2929
exact_chunks_mut_of,
30+
windows
3031
};
3132
use zip::Zip;
3233

@@ -46,6 +47,7 @@ use iter::{
4647
AxisIterMut,
4748
ExactChunks,
4849
ExactChunksMut,
50+
Windows
4951
};
5052
use stacking::stack;
5153
use PrivateNew;
@@ -712,6 +714,18 @@ impl<A, S, D> ArrayBase<S, D> where S: Data<Elem=A>, D: Dimension
712714
exact_chunks_of(self.view(), chunk_size)
713715
}
714716

717+
/// Return a windows iterator.
718+
///
719+
/// Will iterate over no elements if window size is larger
720+
/// than the actual array size of any dimension.
721+
///
722+
/// **Panics** if any dimension of `window_size` is zero.
723+
pub fn windows<E>(&self, window_size: E) -> Windows<A, D>
724+
where E: IntoDimension<Dim=D>
725+
{
726+
windows(self.view(), window_size)
727+
}
728+
715729
#[doc(hidden)]
716730
#[deprecated(note="Renamed to exact_chunks")]
717731
pub fn whole_chunks<E>(&self, chunk_size: E) -> ExactChunks<A, D>

src/iterators/iter.rs

+1
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ pub use iterators::{
3232
ExactChunksIter,
3333
ExactChunksMut,
3434
ExactChunksIterMut,
35+
Windows
3536
};

src/iterators/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#[macro_use] mod macros;
1111
mod chunks;
12+
mod windows;
1213
mod lanes;
1314
pub mod iter;
1415

@@ -30,6 +31,10 @@ use super::{
3031
NdProducer,
3132
};
3233

34+
pub use self::windows::{
35+
Windows,
36+
windows
37+
};
3338
pub use self::chunks::{
3439
ExactChunks,
3540
ExactChunksIter,

src/iterators/windows.rs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
2+
use imp_prelude::*;
3+
use IntoDimension;
4+
5+
pub struct Windows<'a, A: 'a, D> {
6+
iter : ::iter::Iter<'a, A, D>,
7+
window: D,
8+
stride: D,
9+
}
10+
11+
pub fn windows<A, D, E>(a: ArrayView<A, D>, window_size: E) -> Windows<A, D>
12+
where D: Dimension,
13+
E: IntoDimension<Dim=D>,
14+
{
15+
let window = window_size.into_dimension();
16+
ndassert!(a.ndim() == window.ndim(),
17+
concat!("Window dimension {} does not match array dimension {} ",
18+
"(with array of shape {:?})"),
19+
window.ndim(), a.ndim(), a.shape());
20+
let mut size = a.raw_dim();
21+
for (sz, &ws) in size.slice_mut().iter_mut().zip(window.slice())
22+
{
23+
if ws == 0 { panic!("window-size must not be zero!"); }
24+
// cannot use std::cmp::max(0, ..) since arithmetic underflow panics
25+
*sz = if *sz < ws { 0 } else { *sz - ws + 1 };
26+
}
27+
28+
let mut strides = a.raw_dim();
29+
for (a, b) in strides.slice_mut().iter_mut().zip(a.strides()) {
30+
*a = *b as Ix;
31+
}
32+
33+
let mult_strides = strides.clone();
34+
35+
unsafe {
36+
Windows {
37+
iter : ArrayView::from_shape_ptr(size.clone().strides(mult_strides), a.as_ptr()).into_iter(),
38+
window: window,
39+
stride: strides,
40+
}
41+
}
42+
}
43+
44+
impl<'a, A, D> Iterator for Windows<'a, A, D>
45+
where D: Dimension,
46+
{
47+
type Item = ArrayView<'a, A, D>;
48+
fn next(&mut self) -> Option<Self::Item> {
49+
self.iter.next().map(|elt| {
50+
unsafe {
51+
ArrayView::from_shape_ptr(self.window.clone().strides(self.stride.clone()), elt)
52+
}
53+
})
54+
}
55+
}

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,13 @@ mod free_functions;
135135
pub use free_functions::*;
136136
pub use iterators::iter;
137137

138+
mod si;
138139
mod layout;
139140
mod indexes;
140141
mod iterators;
141142
mod linalg_traits;
142143
mod linspace;
143144
mod numeric_util;
144-
mod si;
145145
mod error;
146146
mod shape_builder;
147147
mod stacking;

tests/windows.rs

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
2+
extern crate ndarray;
3+
extern crate itertools;
4+
5+
use ndarray::prelude::*;
6+
7+
// Edge Cases for Windows iterator:
8+
//
9+
// - window size is 0
10+
// - what is the behaviour of the standard for this situation?
11+
// "Panics if size is 0."
12+
// - window size of 1
13+
// - should a warning be printed?
14+
// - what is the behaviour of the standard for this situation?
15+
// => No overlapping for size-1-windows but normal behaviour besides that.
16+
// - window size bigger than actual array size
17+
// - ragged windows or panic?
18+
// - what is the behaviour of the standard for this situation?
19+
// "If the slice is shorter than size, the iterator returns no values."
20+
21+
/// Test that verifies the `Windows` iterator panics on window sizes equal to zero.
22+
#[test]
23+
#[should_panic]
24+
fn windows_iterator_zero_size() {
25+
let a = Array::from_iter(10..37)
26+
.into_shape((3, 3, 3))
27+
.unwrap();
28+
a.windows(Dim((0, 0, 0)));
29+
}
30+
31+
/// Test that verifites that no windows are yielded on oversized window sizes.
32+
#[test]
33+
fn windows_iterator_oversized() {
34+
let a = Array::from_iter(10..37)
35+
.into_shape((3, 3, 3))
36+
.unwrap();
37+
let mut iter = a.windows(Dim((4, 3, 2))); // (4,3,2) doesn't fit into (3,3,3) => oversized!
38+
assert_eq!(iter.next(), None);
39+
}
40+
41+
/// Simple test for iterating 1d-arrays via `Windows`.
42+
#[test]
43+
fn windows_iterator_1d() {
44+
let a = Array::from_iter(10..20).into_shape(10).unwrap();
45+
itertools::assert_equal(
46+
a.windows(Dim(4)),
47+
vec![
48+
arr1(&[10, 11, 12, 13]),
49+
arr1(&[11, 12, 13, 14]),
50+
arr1(&[12, 13, 14, 15]),
51+
arr1(&[13, 14, 15, 16]),
52+
arr1(&[14, 15, 16, 17]),
53+
arr1(&[15, 16, 17, 18]),
54+
arr1(&[16, 17, 18, 19])
55+
]);
56+
}
57+
58+
/// Simple test for iterating 2d-arrays via `Windows`.
59+
#[test]
60+
fn windows_iterator_2d() {
61+
let a = Array::from_iter(10..30).into_shape((5, 4)).unwrap();
62+
itertools::assert_equal(
63+
a.windows(Dim((3, 2))),
64+
vec![
65+
arr2(&[ [10, 11], [14, 15], [18, 19] ]),
66+
arr2(&[ [11, 12], [15, 16], [19, 20] ]),
67+
arr2(&[ [12, 13], [16, 17], [20, 21] ]),
68+
69+
arr2(&[ [14, 15], [18, 19], [22, 23] ]),
70+
arr2(&[ [15, 16], [19, 20], [23, 24] ]),
71+
arr2(&[ [16, 17], [20, 21], [24, 25] ]),
72+
73+
arr2(&[ [18, 19], [22, 23], [26, 27] ]),
74+
arr2(&[ [19, 20], [23, 24], [27, 28] ]),
75+
arr2(&[ [20, 21], [24, 25], [28, 29] ])
76+
]);
77+
}
78+
79+
/// Simple test for iterating 3d-arrays via `Windows`.
80+
#[test]
81+
fn windows_iterator_3d() {
82+
use ndarray::arr3;
83+
let a = Array::from_iter(10..37).into_shape((3, 3, 3)).unwrap();
84+
itertools::assert_equal(
85+
a.windows(Dim((2, 2, 2))),
86+
vec![
87+
arr3(&[ [[10, 11], [13, 14]], [[19, 20], [22, 23]] ]),
88+
arr3(&[ [[11, 12], [14, 15]], [[20, 21], [23, 24]] ]),
89+
90+
arr3(&[ [[13, 14], [16, 17]], [[22, 23], [25, 26]] ]),
91+
arr3(&[ [[14, 15], [17, 18]], [[23, 24], [26, 27]] ]),
92+
93+
arr3(&[ [[19, 20], [22, 23]], [[28, 29], [31, 32]] ]),
94+
arr3(&[ [[20, 21], [23, 24]], [[29, 30], [32, 33]] ]),
95+
96+
arr3(&[ [[22, 23], [25, 26]], [[31, 32], [34, 35]] ]),
97+
arr3(&[ [[23, 24], [26, 27]], [[32, 33], [35, 36]] ]),
98+
]);
99+
}

0 commit comments

Comments
 (0)