ndarray is a Rust crate that provides N-dimensional arrays (tensors) for numerical computing. It is often considered the Rust equivalent to NumPy in Python, offering powerful and efficient data structures and operations for scientific and mathematical computations.
Key characteristics and features of ndarray:
* N-dimensional Arrays: At its core, ndarray provides the `Array<T, D>` struct, representing a multi-dimensional array where `T` is the type of elements (e.g., `f32`, `f64`, `i32`) and `D` is the dimension type (e.g., `Ix1` for 1D, `Ix2` for 2D, `IxDyn` for dynamically sized dimensions).
* Homogeneous Data: All elements within an ndarray must be of the same data type.
* Memory Efficiency: Arrays are stored in a contiguous block of memory. By default, ndarray uses row-major order (C-style), but it can also handle column-major (Fortran-style) layouts and strided arrays, allowing for flexible memory views without copying data.
* Views and Slicing: Operations like slicing and reshaping often return "views" (`ArrayView`, `ArrayViewMut`) into the original data rather than creating copies. This is crucial for performance when working with large datasets. `ArrayView` provides immutable access, while `ArrayViewMut` provides mutable access.
* Broadcasting: ndarray supports broadcasting, a powerful mechanism for performing element-wise operations on arrays of different shapes by implicitly extending the smaller array's dimensions to match the larger one, similar to NumPy.
* Algebraic Operations: It provides a comprehensive set of element-wise arithmetic operations (+, -, *, /), linear algebra functions (e.g., dot product, matrix multiplication), and reductions (sum, mean, min, max).
* Iterators: ndarray integrates well with Rust's iterator system, allowing for efficient element-wise processing and functional-style programming.
* Immutability and Mutability: It distinguishes between immutable array references (`&Array`) and mutable array references (`&mut Array`), enforcing Rust's safety guarantees at compile time.
* Macro for Creation: The `array!` macro provides a convenient way to initialize arrays with a syntax reminiscent of array literals.
ndarray is a foundational library for many scientific computing tasks in Rust, including machine learning, data analysis, and numerical simulations, thanks to its performance, flexibility, and safety features.
Example Code
// First, add ndarray to your Cargo.toml:
// [dependencies]
// ndarray = "0.15"
use ndarray::{array, Array, Array1, Array2, IxDyn, s};
fn main() {
// 1. Array Creation
println!("--- Array Creation ---");
// Create a 1D array (vector) from a Vec
let a: Array1<f64> = Array::from_vec(vec![1.0, 2.0, 3.0, 4.0]);
println!("a (1D): {:?}", a);
// Create a 2D array (matrix) using array! macro
let b: Array2<i32> = array![[1, 2, 3], [4, 5, 6]];
println!("b (2D):\n{}", b);
// Create an array filled with zeros
let c: Array2<f64> = Array2::zeros((2, 3));
println!("c (zeros):\n{}", c);
// Create an array with dynamic dimensions
let d: Array<f64, IxDyn> = Array::from_shape_vec(
[2, 2, 2].into_dyn(), // Shape: 2x2x2
vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0],
)
.unwrap();
println!("d (3D dynamic):\n{}", d);
// 2. Element Access
println!("\n--- Element Access ---");
println!("Element a[0]: {}", a[0]);
println!("Element b[0, 1]: {}", b[[0, 1]]);
// Mutable access
let mut mutable_b = b.clone();
mutable_b[[0, 0]] = 100;
println!("Mutable b after modification:\n{}", mutable_b);
// 3. Slicing
println!("\n--- Slicing ---");
// Slice the first row of b
let row_slice = b.row(0);
println!("First row of b: {:?}", row_slice);
// Slice a sub-matrix (rows 0 to 1, cols 1 to 3)
// s![start..end, start..end] or s![row_index, col_start..col_end]
let sub_matrix = b.slice(s![0..2, 1..3]);
println!("Sub-matrix from b (rows 0-1, cols 1-2):\n{}", sub_matrix);
// 4. Arithmetic Operations (Element-wise)
println!("\n--- Arithmetic Operations ---");
let arr1 = array![[1.0, 2.0], [3.0, 4.0]];
let arr2 = array![[5.0, 6.0], [7.0, 8.0]];
println!("arr1 + arr2:\n{}", &arr1 + &arr2);
println!("arr1 * 2.0:\n{}", &arr1 * 2.0);
// 5. Matrix Multiplication (Dot Product)
println!("\n--- Matrix Multiplication ---");
let mat_a = array![[1.0, 2.0], [3.0, 4.0]];
let mat_b = array![[5.0, 6.0], [7.0, 8.0]];
let dot_product = mat_a.dot(&mat_b);
println!("Dot product (mat_a * mat_b):\n{}", dot_product);
// 6. Reshaping
println!("\n--- Reshaping ---");
let flat_array = array![1, 2, 3, 4, 5, 6];
let reshaped_array = flat_array.into_shape((2, 3)).unwrap();
println!("Original flat array: {:?}", flat_array);
println!("Reshaped to 2x3:\n{}", reshaped_array);
// Note: Reshaping returns a view if possible, otherwise it may copy data.
// .into_shape() consumes the array and returns a new one with the specified shape, potentially reinterpreting strides.
// If you need a view, use .view().reshape() or similar.
}








ndarray