diff --git a/src/impl_methods.rs b/src/impl_methods.rs index de74af795..444d98525 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -83,17 +83,53 @@ where } /// Return the shape of the array as it stored in the array. + /// + /// This is primarily useful for passing to other `ArrayBase` + /// functions, such as when creating another array of the same + /// shape and dimensionality. + /// + /// ``` + /// use ndarray::Array; + /// + /// let a = Array::from_elem((2, 3), 5.); + /// + /// // Create an array of zeros that's the same shape and dimensionality as `a`. + /// let b = Array::::zeros(a.raw_dim()); + /// ``` pub fn raw_dim(&self) -> D { self.dim.clone() } /// Return the shape of the array as a slice. - pub fn shape(&self) -> &[Ix] { + /// + /// Note that you probably don't want to use this to create an array of the + /// same shape as another array because creating an array with e.g. + /// [`Array::zeros()`](ArrayBase::zeros) using a shape of type `&[usize]` + /// results in a dynamic-dimensional array. If you want to create an array + /// that has the same shape and dimensionality as another array, use + /// [`.raw_dim()`](ArrayBase::raw_dim) instead: + /// + /// ```rust + /// use ndarray::{Array, Array2}; + /// + /// let a = Array2::::zeros((3, 4)); + /// let shape = a.shape(); + /// assert_eq!(shape, &[3, 4]); + /// + /// // Since `a.shape()` returned `&[usize]`, we get an `ArrayD` instance: + /// let b = Array::zeros(shape); + /// assert_eq!(a.clone().into_dyn(), b); + /// + /// // To get the same dimension type, use `.raw_dim()` instead: + /// let c = Array::zeros(a.raw_dim()); + /// assert_eq!(a, c); + /// ``` + pub fn shape(&self) -> &[usize] { self.dim.slice() } - /// Return the strides of the array as a slice - pub fn strides(&self) -> &[Ixs] { + /// Return the strides of the array as a slice. + pub fn strides(&self) -> &[isize] { let s = self.strides.slice(); // reinterpret unsigned integer as signed unsafe { diff --git a/src/lib.rs b/src/lib.rs index d998be49f..8b44c21d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -638,6 +638,25 @@ pub type Ixs = isize; /// - `B @ &A` which consumes `B`, updates it with the result, and returns it /// - `C @= &A` which performs an arithmetic operation in place /// +/// Note that the element type needs to implement the operator trait and the +/// `Clone` trait. +/// +/// ``` +/// use ndarray::{array, ArrayView1}; +/// +/// let owned1 = array![1, 2]; +/// let owned2 = array![3, 4]; +/// let view1 = ArrayView1::from(&[5, 6]); +/// let view2 = ArrayView1::from(&[7, 8]); +/// let mut mutable = array![9, 10]; +/// +/// let sum1 = &view1 + &view2; // Allocates a new array. Note the explicit `&`. +/// // let sum2 = view1 + &view2; // This doesn't work because `view1` is not an owned array. +/// let sum3 = owned1 + view1; // Consumes `owned1`, updates it, and returns it. +/// let sum4 = owned2 + &view2; // Consumes `owned2`, updates it, and returns it. +/// mutable += &view2; // Updates `mutable` in-place. +/// ``` +/// /// ### Binary Operators with Array and Scalar /// /// The trait [`ScalarOperand`](trait.ScalarOperand.html) marks types that can be used in arithmetic @@ -920,7 +939,8 @@ pub type Ixs = isize; /// 3Works only if the array is contiguous. /// /// The table above does not include all the constructors; it only shows -/// conversions to/from `Vec`s/slices. See below for more constructors. +/// conversions to/from `Vec`s/slices. See +/// [below](#constructor-methods-for-owned-arrays) for more constructors. /// /// [ArrayView::reborrow()]: type.ArrayView.html#method.reborrow /// [ArrayViewMut::reborrow()]: type.ArrayViewMut.html#method.reborrow @@ -933,6 +953,101 @@ pub type Ixs = isize; /// [.view()]: #method.view /// [.view_mut()]: #method.view_mut /// +/// ### Conversions from Nested `Vec`s/`Array`s +/// +/// It's generally a good idea to avoid nested `Vec`/`Array` types, such as +/// `Vec>` or `Vec>` because: +/// +/// * they require extra heap allocations compared to a single `Array`, +/// +/// * they can scatter data all over memory (because of multiple allocations), +/// +/// * they cause unnecessary indirection (traversing multiple pointers to reach +/// the data), +/// +/// * they don't enforce consistent shape within the nested +/// `Vec`s/`ArrayBase`s, and +/// +/// * they are generally more difficult to work with. +/// +/// The most common case where users might consider using nested +/// `Vec`s/`Array`s is when creating an array by appending rows/subviews in a +/// loop, where the rows/subviews are computed within the loop. However, there +/// are better ways than using nested `Vec`s/`Array`s. +/// +/// If you know ahead-of-time the shape of the final array, the cleanest +/// solution is to allocate the final array before the loop, and then assign +/// the data to it within the loop, like this: +/// +/// ```rust +/// use ndarray::{array, Array2, Axis}; +/// +/// let mut arr = Array2::zeros((2, 3)); +/// for (i, mut row) in arr.axis_iter_mut(Axis(0)).enumerate() { +/// // Perform calculations and assign to `row`; this is a trivial example: +/// row.fill(i); +/// } +/// assert_eq!(arr, array![[0, 0, 0], [1, 1, 1]]); +/// ``` +/// +/// If you don't know ahead-of-time the shape of the final array, then the +/// cleanest solution is generally to append the data to a flat `Vec`, and then +/// convert it to an `Array` at the end with +/// [`::from_shape_vec()`](#method.from_shape_vec). You just have to be careful +/// that the layout of the data (the order of the elements in the flat `Vec`) +/// is correct. +/// +/// ```rust +/// use ndarray::{array, Array2}; +/// +/// # fn main() -> Result<(), Box> { +/// let ncols = 3; +/// let mut data = Vec::new(); +/// let mut nrows = 0; +/// for i in 0..2 { +/// // Compute `row` and append it to `data`; this is a trivial example: +/// let row = vec![i; ncols]; +/// data.extend_from_slice(&row); +/// nrows += 1; +/// } +/// let arr = Array2::from_shape_vec((nrows, ncols), data)?; +/// assert_eq!(arr, array![[0, 0, 0], [1, 1, 1]]); +/// # Ok(()) +/// # } +/// ``` +/// +/// If neither of these options works for you, and you really need to convert +/// nested `Vec`/`Array` instances to an `Array`, the cleanest solution is +/// generally to use +/// [`Iterator::flatten()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.flatten) +/// to get a flat `Vec`, and then convert the `Vec` to an `Array` with +/// [`::from_shape_vec()`](#method.from_shape_vec), like this: +/// +/// ```rust +/// use ndarray::{array, Array2, Array3}; +/// +/// # fn main() -> Result<(), Box> { +/// let nested: Vec> = vec![ +/// array![[1, 2, 3], [4, 5, 6]], +/// array![[7, 8, 9], [10, 11, 12]], +/// ]; +/// let inner_shape = nested[0].dim(); +/// let shape = (nested.len(), inner_shape.0, inner_shape.1); +/// let flat: Vec = nested.iter().flatten().cloned().collect(); +/// let arr = Array3::from_shape_vec(shape, flat)?; +/// assert_eq!(arr, array![ +/// [[1, 2, 3], [4, 5, 6]], +/// [[7, 8, 9], [10, 11, 12]], +/// ]); +/// # Ok(()) +/// # } +/// ``` +/// +/// Note that this implementation assumes that the nested `Vec`s are all the +/// same shape and that the `Vec` is non-empty. Depending on your application, +/// it may be a good idea to add checks for these assumptions and possibly +/// choose a different way to handle the empty case. +/// // # For implementors // // All methods must uphold the following constraints: diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 289815d30..87e7be590 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -115,13 +115,13 @@ impl ArrayBase /// ``` /// use ndarray::{aview0, aview1, arr2, Axis}; /// - /// let a = arr2(&[[1., 2.], - /// [3., 4.]]); + /// let a = arr2(&[[1., 2., 3.], + /// [4., 5., 6.]]); /// assert!( - /// a.sum_axis(Axis(0)) == aview1(&[4., 6.]) && - /// a.sum_axis(Axis(1)) == aview1(&[3., 7.]) && + /// a.sum_axis(Axis(0)) == aview1(&[5., 7., 9.]) && + /// a.sum_axis(Axis(1)) == aview1(&[6., 15.]) && /// - /// a.sum_axis(Axis(0)).sum_axis(Axis(0)) == aview0(&10.) + /// a.sum_axis(Axis(0)).sum_axis(Axis(0)) == aview0(&21.) /// ); /// ``` /// @@ -156,13 +156,15 @@ impl ArrayBase /// fails for the axis length. /// /// ``` - /// use ndarray::{aview1, arr2, Axis}; + /// use ndarray::{aview0, aview1, arr2, Axis}; /// - /// let a = arr2(&[[1., 2.], - /// [3., 4.]]); + /// let a = arr2(&[[1., 2., 3.], + /// [4., 5., 6.]]); /// assert!( - /// a.mean_axis(Axis(0)).unwrap() == aview1(&[2.0, 3.0]) && - /// a.mean_axis(Axis(1)).unwrap() == aview1(&[1.5, 3.5]) + /// a.mean_axis(Axis(0)).unwrap() == aview1(&[2.5, 3.5, 4.5]) && + /// a.mean_axis(Axis(1)).unwrap() == aview1(&[2., 5.]) && + /// + /// a.mean_axis(Axis(0)).unwrap().mean_axis(Axis(0)).unwrap() == aview0(&3.5) /// ); /// ``` pub fn mean_axis(&self, axis: Axis) -> Option> @@ -260,9 +262,9 @@ impl ArrayBase /// The standard deviation is defined as: /// /// ```text - /// 1 n - /// stddev = sqrt ( ―――――――― ∑ (xᵢ - x̅)² ) - /// n - ddof i=1 + /// ⎛ 1 n ⎞ + /// stddev = sqrt ⎜ ―――――――― ∑ (xᵢ - x̅)²⎟ + /// ⎝ n - ddof i=1 ⎠ /// ``` /// /// where