Expand description
ndarray
for NumPy users.
This is an introductory guide to ndarray
for people with experience using
NumPy, although it may also be useful to others. For a more general
introduction to ndarray
’s array type ArrayBase
, see the ArrayBase
docs.
§Contents
§Similarities
ndarray
’s array type (ArrayBase
), is very similar to
NumPy’s array type (numpy.ndarray
):
- Arrays have a single element type.
- Arrays can have arbitrarily many dimensions.
- Arrays can have arbitrary strides.
- Indexing starts at zero, not one.
- The default memory layout is row-major, and the default iterators follow row-major order (also called “logical order” in the documentation).
- Arithmetic operators work elementwise. (For example,
a * b
performs elementwise multiplication, not matrix multiplication.) - Owned arrays are contiguous in memory.
- Many operations, such as slicing, are very cheap because they can return a view of an array instead of copying the data.
NumPy has many features that ndarray
doesn’t have yet, such as:
- index arrays
- mask index arrays
- co-broadcasting (
ndarray
only supports broadcasting the right-hand array in a binary operation.)
§Some key differences
NumPy |
|
---|---|
In NumPy, there is no distinction between owned arrays, views, and mutable
views. There can be multiple arrays (instances of |
In |
In NumPy, all arrays are dynamic-dimensional. |
In |
When slicing in NumPy, the indices are |
When slicing in |
§The ndarray ecosystem
ndarray
does not provide advanced linear algebra routines out of the box (e.g. SVD decomposition).
Most of the routines that you can find in scipy.linalg
/numpy.linalg
are provided by another crate,
ndarray-linalg
.
The same holds for statistics: ndarray
provides some basic functionalities (e.g. mean
)
but more advanced routines can be found in ndarray-stats
.
If you are looking to generate random arrays instead, check out ndarray-rand
.
It is also possible to serialize NumPy
arrays in .npy
/.npz
format and deserialize them as ndarray
arrays (and vice versa)
using ndarray-npy
.
§Other Rust array/matrix crates
Of the array/matrix types in Rust crates, the ndarray
array type is probably
the most similar to NumPy’s arrays and is the most flexible. However, if your
use-case is constrained to linear algebra on 1-D and 2-D vectors and matrices,
it might be worth considering other crates:
nalgebra
provides 1-D and 2-D column-major vector and matrix types for linear algebra. Vectors and matrices can have constant or dynamic shapes, andnalgebra
uses the type system to provide compile-time checking of shapes, not just the number of dimensions.nalgebra
provides convenient functionality for geometry (e.g. coordinate transformations) and linear algebra.cgmath
provides 1-D and 2-D column-major types of shape 4×4 or smaller. It’s primarily designed for computer graphics and provides convenient functionality for geometry (e.g. coordinate transformations). Similar tonalgebra
,cgmath
uses the type system to provide compile-time checking of shapes.rulinalg
provides 1-D and 2-D row-major vector and matrix types with dynamic shapes. Similar tondarray
,rulinalg
provides compile-time checking of the number of dimensions, but not shapes.rulinalg
provides pure-Rust implementations of linear algebra operations.- If there’s another crate that should be listed here, please let us know.
In contrast to these crates, ndarray
provides an n-dimensional array type,
so it’s not restricted to 1-D and 2-D vectors and matrices. Also, operators
operate elementwise by default, so the multiplication operator *
performs
elementwise multiplication instead of matrix multiplication. (You have to
specifically call .dot()
if you want matrix multiplication.)
§Rough ndarray
–NumPy equivalents
These tables provide some rough equivalents of NumPy operations in ndarray
.
There are a variety of other methods that aren’t included in these tables,
including shape-manipulation, array creation, and iteration routines.
It’s assumed that you’ve imported NumPy like this:
import numpy as np
and ndarray
like this:
use ndarray::prelude::*;
§Array creation
This table contains ways to create arrays from scratch. For creating arrays by
operations on other arrays (e.g. arithmetic), see the other tables. Also see
the ::from_vec()
, ::from_iter()
,
::default()
, ::from_shape_fn()
, and
::from_shape_vec_unchecked()
methods.
NumPy | ndarray | Notes |
---|---|---|
np.array([[1.,2.,3.], [4.,5.,6.]]) | array![[1.,2.,3.], [4.,5.,6.]] or arr2(&[[1.,2.,3.], [4.,5.,6.]]) | 2×3 floating-point array literal |
np.arange(0., 10., 0.5) or np.r_[:10.:0.5] | Array::range(0., 10., 0.5) | create a 1-D array with values 0. , 0.5 , …, 9.5 |
np.linspace(0., 10., 11) or np.r_[:10.:11j] | Array::linspace(0., 10., 11) | create a 1-D array with 11 elements with values 0. , …, 10. |
np.logspace(2.0, 3.0, num=4, base=10.0) | Array::logspace(10.0, 2.0, 3.0, 4) | create a 1-D array with 4 elements with values 100. , 215.4 , 464.1 , 1000. |
np.geomspace(1., 1000., num=4) | Array::geomspace(1e0, 1e3, 4) | create a 1-D array with 4 elements with values 1. , 10. , 100. , 1000. |
np.ones((3, 4, 5)) | Array::ones((3, 4, 5)) | create a 3×4×5 array filled with ones (inferring the element type) |
np.zeros((3, 4, 5)) | Array::zeros((3, 4, 5)) | create a 3×4×5 array filled with zeros (inferring the element type) |
np.zeros((3, 4, 5), order='F') | Array::zeros((3, 4, 5).f()) | create a 3×4×5 array with Fortran (column-major) memory layout filled with zeros (inferring the element type) |
np.zeros_like(a, order='C') | Array::zeros(a.raw_dim()) | create an array of zeros of the shape shape as a , with row-major memory layout (unlike NumPy, this infers the element type from context instead of duplicating a ’s element type) |
np.full((3, 4), 7.) | Array::from_elem((3, 4), 7.) | create a 3×4 array filled with the value 7. |
np.eye(3) | Array::eye(3) | create a 3×3 identity matrix (inferring the element type) |
np.diag(np.array([1, 2, 3])) | Array2::from_diag(&arr1(&[1, 2, 3])) | create a 3×3 matrix with [1, 2, 3] as diagonal and zeros elsewhere (inferring the element type) |
np.array([1, 2, 3, 4]).reshape((2, 2)) | Array::from_shape_vec((2, 2), vec![1, 2, 3, 4])? | create a 2×2 array from the elements in the list/Vec |
np.array([1, 2, 3, 4]).reshape((2, 2), order='F') | Array::from_shape_vec((2, 2).f(), vec![1, 2, 3, 4])? | create a 2×2 array from the elements in the list/Vec using Fortran (column-major) order |
np.random | See the ndarray-rand crate. | create arrays of random numbers |
Note that the examples in the table rely on the compiler inferring the
element type and dimensionality from context, which is usually sufficient.
However, if the compiler cannot infer the types, you can specify them
manually. These are examples of creating a 3-D Fortran-layout array of
f64
s:
// This is an example where the compiler can infer the element type
// because `f64::sin` can only be called on `f64` elements:
let arr1 = Array::zeros((3, 2, 4).f());
arr1.mapv(f64::sin);
// Specify just the element type and infer the dimensionality:
let arr2 = Array::<f64, _>::zeros((3, 2, 4).f());
let arr3: Array<f64, _> = Array::zeros((3, 2, 4).f());
// Specify both the element type and dimensionality:
let arr4 = Array3::<f64>::zeros((3, 2, 4).f());
let arr5: Array3<f64> = Array::zeros((3, 2, 4).f());
let arr6 = Array::<f64, Ix3>::zeros((3, 2, 4).f());
let arr7: Array<f64, Ix3> = Array::zeros((3, 2, 4).f());
§Indexing and slicing
A few notes:
-
Indices start at 0. For example, “row 1” is the second row in the array.
-
Some methods have multiple variants in terms of ownership and mutability. Only the non-mutable methods that take the array by reference are listed in this table. For example,
.slice()
also has corresponding methods.slice_mut()
,.slice_move()
, and.slice_collapse()
. -
The behavior of slicing is different from NumPy for slices with
step < 0
. See the docs for thes![]
macro for more details.
NumPy | ndarray | Notes |
---|---|---|
a[-1] | a[a.len() - 1] | access the last element in 1-D array a |
a[1, 4] | a[[1, 4]] | access the element in row 1, column 4 |
a[1] or a[1, :, :] | a.slice(s![1, .., ..]) or a.index_axis(Axis(0), 1) | get a 2-D subview of a 3-D array at index 1 of axis 0 |
a[0:5] or a[:5] or a[0:5, :] | a.slice(s![0..5, ..]) or a.slice(s![..5, ..]) or a.slice_axis(Axis(0), Slice::from(0..5)) | get the first 5 rows of a 2-D array |
a[-5:] or a[-5:, :] | a.slice(s![-5.., ..]) or a.slice_axis(Axis(0), Slice::from(-5..)) | get the last 5 rows of a 2-D array |
a[:3, 4:9] | a.slice(s![..3, 4..9]) | columns 4, 5, 6, 7, and 8 of the first 3 rows |
a[1:4:2, ::-1] | a.slice(s![1..4;2, ..;-1]) | rows 1 and 3 with the columns in reverse order |
a.take([4, 2]) | a.select(Axis(0), &[4, 2]) | rows 4 and 2 of the array |
§Shape and strides
Note that a.shape()
, a.dim()
, and
a.raw_dim()
all return the shape of the array, but as
different types. a.shape()
returns the shape as &[Ix]
, (where
Ix
is usize
) which is useful for general operations on the shape.
a.dim()
returns the shape as D::Pattern
, which is useful for
pattern-matching shapes. a.raw_dim()
returns the shape as D
, which is
useful for creating other arrays of the same shape.
NumPy | ndarray | Notes |
---|---|---|
np.ndim(a) or a.ndim | a.ndim() | get the number of dimensions of array a |
np.size(a) or a.size | a.len() | get the number of elements in array a |
np.shape(a) or a.shape | a.shape() or a.dim() | get the shape of array a |
a.shape[axis] | a.len_of(Axis(axis)) | get the length of an axis |
a.strides | a.strides() | get the strides of array a |
np.size(a) == 0 or a.size == 0 | a.is_empty() | check if the array has zero elements |
§Mathematics
Note that .mapv()
has corresponding methods .map()
,
.mapv_into()
, .map_inplace()
, and
.mapv_inplace()
. Also look at .fold()
,
.for_each()
, .fold_axis()
, and
.map_axis()
.
NumPy |
|
Notes |
---|---|---|
|
transpose of array | |
|
2-D matrix multiply | |
|
2-D matrix dot 1-D column vector | |
|
1-D row vector dot 2-D matrix | |
|
vector dot product | |
|
element-wise arithmetic operations | |
|
element-wise power of 3 | |
|
element-wise square root for | |
|
array of | |
|
sum the elements in | |
|
sum the elements in | |
|
calculate the mean of the elements in | |
|
calculate the mean of the elements in | |
|
check if the arrays’ elementwise differences are within an absolute tolerance (it requires the | |
|
view the diagonal of | |
|
See |
linear algebra (matrix inverse, solving, decompositions, etc.) |
§Array manipulation
NumPy | ndarray | Notes |
---|---|---|
a[:] = 3. | a.fill(3.) | set all array elements to the same scalar value |
a[:] = b | a.assign(&b) | copy the data from array b into array a |
a[:5, 2] = 3. | a.slice_mut(s![..5, 2]).fill(3.) | set a portion of the array to the same scalar value |
a[:5, 2] = b | a.slice_mut(s![..5, 2]).assign(&b) | copy the data from array b into part of array a |
np.concatenate((a,b), axis=1) | concatenate![Axis(1), a, b] or concatenate(Axis(1), &[a.view(), b.view()]) | concatenate arrays a and b along axis 1 |
np.stack((a,b), axis=1) | stack![Axis(1), a, b] or stack(Axis(1), vec![a.view(), b.view()]) | stack arrays a and b along axis 1 |
a[:,np.newaxis] or np.expand_dims(a, axis=1) | a.slice(s![.., NewAxis]) or a.insert_axis(Axis(1)) | create an view of 1-D array a , inserting a new axis 1 |
a.transpose() or a.T | a.t() or a.reversed_axes() | transpose of array a (view for .t() or by-move for .reversed_axes() ) |
np.diag(a) | a.diag() | view the diagonal of a |
a.flatten() | use std::iter::FromIterator; Array::from_iter(a.iter().cloned()) | create a 1-D array by flattening a |
§Iteration
ndarray
has lots of interesting iterators/producers that implement the
NdProducer
trait, which is a generalization of Iterator
to multiple dimensions. This makes it possible to correctly and efficiently
zip together slices/subviews of arrays in multiple dimensions with
Zip
or azip!()
. The purpose of this is similar to
np.nditer
,
but Zip
is implemented and used somewhat differently.
This table lists some of the iterators/producers which have a direct
equivalent in NumPy. For a more complete introduction to producers and
iterators, see Loops, Producers, and
Iterators.
Note that there are also variants of these iterators (with a _mut
suffix)
that yield ArrayViewMut
instead of ArrayView
.
NumPy | ndarray | Notes |
---|---|---|
a.flat | a.iter() | iterator over the array elements in logical order |
np.ndenumerate(a) | a.indexed_iter() | flat iterator yielding the index along with each element reference |
iter(a) | a.outer_iter() or a.axis_iter(Axis(0)) | iterator over the first (outermost) axis, yielding each subview |
§Type conversions
In ndarray
, conversions between datatypes are done with mapv()
by
passing a closure to convert every element independently.
For the conversion itself, we have several options:
std::convert::From
ensures lossless, safe conversions at compile-time and is generally recommended.std::convert::TryFrom
can be used for potentially unsafe conversions. It will return aResult
which can be handled orunwrap()
ed to panic if any value at runtime cannot be converted losslessly.- The
as
keyword compiles to lossless/lossy conversions depending on the source and target datatypes. It can be useful whenTryFrom
is a performance issue or does not apply. A notable difference to NumPy is thatas
performs a saturating cast when casting from floats to integers. Further information can be found in the reference on type cast expressions.
For details, be sure to check out the type conversion examples.
NumPy |
|
Notes |
---|---|---|
|
|
convert |
|
|
upcast |
|
|
try to convert |
|
|
convert |
§Convenience methods for 2-D arrays
NumPy | ndarray | Notes |
---|---|---|
len(a) or a.shape[0] | a.nrows() | get the number of rows in a 2-D array |
a.shape[1] | a.ncols() | get the number of columns in a 2-D array |
a[1] or a[1,:] | a.row(1) or a.row_mut(1) | view (or mutable view) of row 1 in a 2-D array |
a[:,4] | a.column(4) or a.column_mut(4) | view (or mutable view) of column 4 in a 2-D array |
a.shape[0] == a.shape[1] | a.is_square() | check if the array is square |
Modules§
- coord_
transform - Example of rotation with Euler angles.
- rk_step
- Example of translating
rk_step
function from SciPy. - simple_
math - Example of simple math operations on 2-D arrays.