Skip to content

Commit 860366d

Browse files
committed
Add support for individual indices when slicing
1 parent 13c9215 commit 860366d

File tree

8 files changed

+496
-170
lines changed

8 files changed

+496
-170
lines changed

src/dimension/dimension_trait.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use std::ops::{Add, Sub, Mul, AddAssign, SubAssign, MulAssign};
1313

1414
use itertools::{enumerate, zip};
1515

16-
use {Ix, Ixs, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, Dim, Si, IxDynImpl};
16+
use {Ix, Ixs, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, Dim, SliceOrIndex, IxDynImpl};
1717
use IntoDimension;
1818
use RemoveAxis;
1919
use {ArrayView1, ArrayViewMut1};
@@ -52,14 +52,14 @@ pub trait Dimension : Clone + Eq + Debug + Send + Sync + Default +
5252
/// size, which you pass by reference. For the dynamic dimension it is
5353
/// a slice.
5454
///
55-
/// - For `Ix1`: `[Si; 1]`
56-
/// - For `Ix2`: `[Si; 2]`
55+
/// - For `Ix1`: `[SliceOrIndex; 1]`
56+
/// - For `Ix2`: `[SliceOrIndex; 2]`
5757
/// - and so on..
58-
/// - For `IxDyn`: `[Si]`
58+
/// - For `IxDyn`: `[SliceOrIndex]`
5959
///
60-
/// The easiest way to create a `&SliceArg` is using the macro
61-
/// [`s![]`](macro.s!.html).
62-
type SliceArg: ?Sized + AsRef<[Si]>;
60+
/// The easiest way to create a `&SliceInfo<SliceArg, Do>` is using the
61+
/// [`s![]`](macro.s!.html) macro.
62+
type SliceArg: ?Sized + AsRef<[SliceOrIndex]>;
6363
/// Pattern matching friendly form of the dimension value.
6464
///
6565
/// - For `Ix1`: `usize`,
@@ -364,7 +364,7 @@ macro_rules! impl_insert_axis_array(
364364

365365
impl Dimension for Dim<[Ix; 0]> {
366366
const NDIM: Option<usize> = Some(0);
367-
type SliceArg = [Si; 0];
367+
type SliceArg = [SliceOrIndex; 0];
368368
type Pattern = ();
369369
type Smaller = Self;
370370
type Larger = Ix1;
@@ -401,7 +401,7 @@ impl Dimension for Dim<[Ix; 0]> {
401401

402402
impl Dimension for Dim<[Ix; 1]> {
403403
const NDIM: Option<usize> = Some(1);
404-
type SliceArg = [Si; 1];
404+
type SliceArg = [SliceOrIndex; 1];
405405
type Pattern = Ix;
406406
type Smaller = Ix0;
407407
type Larger = Ix2;
@@ -495,7 +495,7 @@ impl Dimension for Dim<[Ix; 1]> {
495495

496496
impl Dimension for Dim<[Ix; 2]> {
497497
const NDIM: Option<usize> = Some(2);
498-
type SliceArg = [Si; 2];
498+
type SliceArg = [SliceOrIndex; 2];
499499
type Pattern = (Ix, Ix);
500500
type Smaller = Ix1;
501501
type Larger = Ix3;
@@ -631,7 +631,7 @@ impl Dimension for Dim<[Ix; 2]> {
631631

632632
impl Dimension for Dim<[Ix; 3]> {
633633
const NDIM: Option<usize> = Some(3);
634-
type SliceArg = [Si; 3];
634+
type SliceArg = [SliceOrIndex; 3];
635635
type Pattern = (Ix, Ix, Ix);
636636
type Smaller = Ix2;
637637
type Larger = Ix4;
@@ -749,7 +749,7 @@ macro_rules! large_dim {
749749
($n:expr, $name:ident, $pattern:ty, $larger:ty, { $($insert_axis:tt)* }) => (
750750
impl Dimension for Dim<[Ix; $n]> {
751751
const NDIM: Option<usize> = Some($n);
752-
type SliceArg = [Si; $n];
752+
type SliceArg = [SliceOrIndex; $n];
753753
type Pattern = $pattern;
754754
type Smaller = Dim<[Ix; $n - 1]>;
755755
type Larger = $larger;
@@ -801,7 +801,7 @@ large_dim!(6, Ix6, (Ix, Ix, Ix, Ix, Ix, Ix), IxDyn, {
801801
impl Dimension for IxDyn
802802
{
803803
const NDIM: Option<usize> = None;
804-
type SliceArg = [Si];
804+
type SliceArg = [SliceOrIndex];
805805
type Pattern = Self;
806806
type Smaller = Self;
807807
type Larger = Self;

src/dimension/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ pub fn do_sub<A, D: Dimension>(dims: &mut D, ptr: &mut *mut A, strides: &D,
214214

215215
/// Compute the equivalent unsigned index given the axis length and signed index.
216216
#[inline]
217-
fn abs_index(len: Ix, index: Ixs) -> Ix {
217+
pub fn abs_index(len: Ix, index: Ixs) -> Ix {
218218
if index < 0 {
219219
len - (-index as Ix)
220220
} else {

src/impl_methods.rs

+86-27
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use dimension;
1919
use iterators;
2020
use error::{self, ShapeError, ErrorKind};
2121
use dimension::IntoDimension;
22-
use dimension::{axes_of, Axes, do_slice, merge_axes, stride_offset};
22+
use dimension::{abs_index, axes_of, Axes, do_slice, merge_axes, stride_offset};
2323
use iterators::{
2424
new_lanes,
2525
new_lanes_mut,
@@ -31,7 +31,8 @@ use zip::Zip;
3131

3232
use {
3333
NdIndex,
34-
Si,
34+
SliceInfo,
35+
SliceOrIndex
3536
};
3637
use iter::{
3738
AxisChunksIter,
@@ -207,53 +208,111 @@ impl<A, S, D> ArrayBase<S, D> where S: Data<Elem=A>, D: Dimension
207208
}
208209

209210

210-
/// Return a sliced array.
211+
/// Return a sliced view of the array.
211212
///
212213
/// See [*Slicing*](#slicing) for full documentation.
213-
/// See also [`D::SliceArg`].
214+
/// See also [`SliceInfo`] and [`D::SliceArg`].
214215
///
216+
/// [`SliceInfo`]: struct.SliceInfo.html
215217
/// [`D::SliceArg`]: trait.Dimension.html#associatedtype.SliceArg
216218
///
217-
/// **Panics** if an index is out of bounds or stride is zero.<br>
218-
/// (**Panics** if `D` is `IxDyn` and `indexes` does not match the number of array axes.)
219-
pub fn slice(&self, indexes: &D::SliceArg) -> ArrayView<A, D> {
220-
let mut arr = self.view();
221-
arr.slice_inplace(indexes);
222-
arr
219+
/// **Panics** if an index is out of bounds or step size is zero.<br>
220+
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.)
221+
pub fn slice<Do>(&self, info: &SliceInfo<D::SliceArg, Do>) -> ArrayView<A, Do>
222+
where
223+
Do: Dimension,
224+
{
225+
self.view().slice_move(info)
223226
}
224227

225228
/// Return a sliced read-write view of the array.
226229
///
227-
/// See also [`D::SliceArg`].
230+
/// See [*Slicing*](#slicing) for full documentation.
231+
/// See also [`SliceInfo`] and [`D::SliceArg`].
228232
///
233+
/// [`SliceInfo`]: struct.SliceInfo.html
229234
/// [`D::SliceArg`]: trait.Dimension.html#associatedtype.SliceArg
230235
///
231-
/// **Panics** if an index is out of bounds or stride is zero.<br>
232-
/// (**Panics** if `D` is `IxDyn` and `indexes` does not match the number of array axes.)
233-
pub fn slice_mut(&mut self, indexes: &D::SliceArg) -> ArrayViewMut<A, D>
234-
where S: DataMut
236+
/// **Panics** if an index is out of bounds or step size is zero.<br>
237+
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.)
238+
pub fn slice_mut<Do>(&mut self, info: &SliceInfo<D::SliceArg, Do>) -> ArrayViewMut<A, Do>
239+
where
240+
Do: Dimension,
241+
S: DataMut,
242+
{
243+
self.view_mut().slice_move(info)
244+
}
245+
246+
/// Slice the array, possibly changing the number of dimensions.
247+
///
248+
/// See [*Slicing*](#slicing) for full documentation.
249+
/// See also [`SliceInfo`] and [`D::SliceArg`].
250+
///
251+
/// [`SliceInfo`]: struct.SliceInfo.html
252+
/// [`D::SliceArg`]: trait.Dimension.html#associatedtype.SliceArg
253+
///
254+
/// **Panics** if an index is out of bounds or step size is zero.<br>
255+
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.)
256+
pub fn slice_move<Do>(mut self, info: &SliceInfo<D::SliceArg, Do>) -> ArrayBase<S, Do>
257+
where
258+
Do: Dimension,
235259
{
236-
let mut arr = self.view_mut();
237-
arr.slice_inplace(indexes);
238-
arr
260+
// Slice and subview in-place without changing the number of dimensions.
261+
self.slice_inplace(&*info);
262+
263+
let indices: &[SliceOrIndex] = (**info).as_ref();
264+
265+
// Copy the dim and strides that remain after removing the subview axes.
266+
let out_ndim = info.out_ndim();
267+
let mut new_dim = Do::zero_index_with_ndim(out_ndim);
268+
let mut new_strides = Do::zero_index_with_ndim(out_ndim);
269+
izip!(self.dim.slice(), self.strides.slice(), indices)
270+
.filter_map(|(d, s, slice_or_index)| match slice_or_index {
271+
&SliceOrIndex::Slice(..) => Some((d, s)),
272+
&SliceOrIndex::Index(_) => None,
273+
})
274+
.zip(izip!(new_dim.slice_mut(), new_strides.slice_mut()))
275+
.for_each(|((d, s), (new_d, new_s))| {
276+
*new_d = *d;
277+
*new_s = *s;
278+
});
279+
280+
ArrayBase {
281+
ptr: self.ptr,
282+
data: self.data,
283+
dim: new_dim,
284+
strides: new_strides,
285+
}
239286
}
240287

241-
/// Slice the array’s view in place.
288+
/// Slice the array in place without changing the number of dimensions.
289+
///
290+
/// Note that [`&SliceInfo`](struct.SliceInfo.html) (produced by the
291+
/// [`s![]`](macro.s!.html) macro) will usually coerce into `&D::SliceArg`
292+
/// automatically, but in some cases (e.g. if `D` is `IxDyn`), you may need
293+
/// to call `.as_ref()`.
242294
///
295+
/// See [*Slicing*](#slicing) for full documentation.
243296
/// See also [`D::SliceArg`].
244297
///
245298
/// [`D::SliceArg`]: trait.Dimension.html#associatedtype.SliceArg
246299
///
247-
/// **Panics** if an index is out of bounds or stride is zero.<br>
248-
/// (**Panics** if `D` is `IxDyn` and `indexes` does not match the number of array axes.)
249-
pub fn slice_inplace(&mut self, indexes: &D::SliceArg) {
250-
let indexes: &[Si] = indexes.as_ref();
251-
assert_eq!(indexes.len(), self.ndim());
252-
indexes
300+
/// **Panics** if an index is out of bounds or step size is zero.<br>
301+
/// (**Panics** if `D` is `IxDyn` and `indices` does not match the number of array axes.)
302+
pub fn slice_inplace(&mut self, indices: &D::SliceArg) {
303+
let indices: &[SliceOrIndex] = indices.as_ref();
304+
assert_eq!(indices.len(), self.ndim());
305+
indices
253306
.iter()
254307
.enumerate()
255-
.for_each(|(axis, &Si(start, end, step))| {
256-
self.slice_axis_inplace(Axis(axis), start, end, step)
308+
.for_each(|(axis, slice_or_index)| match slice_or_index {
309+
&SliceOrIndex::Slice(start, end, step) => {
310+
self.slice_axis_inplace(Axis(axis), start, end, step)
311+
}
312+
&SliceOrIndex::Index(index) => {
313+
let i_usize = abs_index(self.len_of(Axis(axis)), index);
314+
self.subview_inplace(Axis(axis), i_usize)
315+
}
257316
});
258317
}
259318

src/lib.rs

+37-10
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ pub use dimension::NdIndex;
105105
pub use dimension::IxDynImpl;
106106
pub use indexes::{indices, indices_of};
107107
pub use error::{ShapeError, ErrorKind};
108-
pub use slice::{Si, S};
108+
pub use slice::{SliceInfo, SliceNextDim, SliceOrIndex};
109109

110110
use iterators::Baseiter;
111111
use iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut};
@@ -422,24 +422,30 @@ pub type Ixs = isize;
422422
///
423423
/// You can use slicing to create a view of a subset of the data in
424424
/// the array. Slicing methods include [`.slice()`], [`.slice_mut()`],
425-
/// and [`.slice_inplace()`].
425+
/// [`.slice_move()`], and [`.slice_inplace()`].
426426
///
427427
/// The slicing argument can be passed using the macro [`s![]`](macro.s!.html),
428-
/// which will be used in all examples. (The explicit form is a reference
429-
/// to a fixed size array of [`Si`]; see its docs for more information.)
428+
/// which will be used in all examples. (The explicit form is an instance of
429+
/// [`&SliceInfo`]; see its docs for more information.)
430430
///
431-
/// [`Si`]: struct.Si.html
431+
/// [`&SliceInfo`]: struct.SliceInfo.html
432+
///
433+
/// If a range is used, the axis is preserved. If an index is used, a subview
434+
/// is taken with respect to the axis. See [*Subviews*](#subviews) for more
435+
/// information about subviews. Note that [`.slice_inplace()`] behaves like
436+
/// [`.subview_inplace()`] by preserving the number of dimensions.
432437
///
433438
/// [`.slice()`]: #method.slice
434439
/// [`.slice_mut()`]: #method.slice_mut
440+
/// [`.slice_move()`]: #method.slice_move
435441
/// [`.slice_inplace()`]: #method.slice_inplace
436442
///
437443
/// ```
438444
/// // import the s![] macro
439445
/// #[macro_use(s)]
440446
/// extern crate ndarray;
441447
///
442-
/// use ndarray::arr3;
448+
/// use ndarray::{arr2, arr3};
443449
///
444450
/// fn main() {
445451
///
@@ -460,8 +466,6 @@ pub type Ixs = isize;
460466
/// // - Every element in each row: `..`
461467
///
462468
/// let b = a.slice(s![.., 0..1, ..]);
463-
/// // without the macro, the explicit argument is `&[S, Si(0, Some(1), 1), S]`
464-
///
465469
/// let c = arr3(&[[[ 1, 2, 3]],
466470
/// [[ 7, 8, 9]]]);
467471
/// assert_eq!(b, c);
@@ -476,14 +480,28 @@ pub type Ixs = isize;
476480
/// let e = arr3(&[[[ 6, 5, 4]],
477481
/// [[12, 11, 10]]]);
478482
/// assert_eq!(d, e);
483+
/// assert_eq!(d.shape(), &[2, 1, 3]);
484+
///
485+
/// // Let’s create a slice while taking a subview with
486+
/// //
487+
/// // - Both submatrices of the greatest dimension: `..`
488+
/// // - The last row in each submatrix, removing that axis: `-1`
489+
/// // - Row elements in reverse order: `..;-1`
490+
/// let f = a.slice(s![.., -1, ..;-1]);
491+
/// let g = arr2(&[[ 6, 5, 4],
492+
/// [12, 11, 10]]);
493+
/// assert_eq!(f, g);
494+
/// assert_eq!(f.shape(), &[2, 3]);
479495
/// }
480496
/// ```
481497
///
482498
/// ## Subviews
483499
///
484500
/// Subview methods allow you to restrict the array view while removing one
485501
/// axis from the array. Subview methods include [`.subview()`],
486-
/// [`.subview_mut()`], [`.into_subview()`], and [`.subview_inplace()`].
502+
/// [`.subview_mut()`], [`.into_subview()`], and [`.subview_inplace()`]. You
503+
/// can also take a subview by using a single index instead of a range when
504+
/// slicing.
487505
///
488506
/// Subview takes two arguments: `axis` and `index`.
489507
///
@@ -493,7 +511,11 @@ pub type Ixs = isize;
493511
/// [`.subview_inplace()`]: #method.subview_inplace
494512
///
495513
/// ```
496-
/// use ndarray::{arr3, aview2, Axis};
514+
/// #[macro_use(s)] extern crate ndarray;
515+
///
516+
/// use ndarray::{arr3, aview1, aview2, Axis};
517+
///
518+
/// # fn main() {
497519
///
498520
/// // 2 submatrices of 2 rows with 3 elements per row, means a shape of `[2, 2, 3]`.
499521
///
@@ -523,6 +545,11 @@ pub type Ixs = isize;
523545
///
524546
/// assert_eq!(sub_col, aview2(&[[ 1, 4],
525547
/// [ 7, 10]]));
548+
///
549+
/// // You can take multiple subviews at once (and slice at the same time)
550+
/// let double_sub = a.slice(s![1, .., 0]);
551+
/// assert_eq!(double_sub, aview1(&[7, 10]));
552+
/// # }
526553
/// ```
527554
///
528555
/// [`.subview_inplace()`] modifies the view in the same way as [`.subview()`],

0 commit comments

Comments
 (0)