xosxos
xosxos

Reputation: 207

How do I create a two-dimensional array from a vector of vectors with ndarray?

I am trying to use the ndarray crate to do some bioinformatics, but I can't seem to be able to create a matrix dynamically.

I have vectors of booleans that I would like to combine into a two-dimensional array. However, trying to flatten the vectors and using into_shape does not retain a correct order of the elements.

Thus I tried to create an empty array and concatenate rows into it, however this gives me an error I cannot comprehend. I know the empty array does not have the same dimensions but I cannot find a way to cast an empty array to the correct type and dimensions.

use ndarray::{concatenate, Array, Axis, Ix2};

fn main() {
    #[rustfmt::skip]
    let vector_of_vectors = vec![
        vec![true, false, true],
        vec![false, true, false],
    ];

    let mut matrix: Array<bool, Ix2> = ndarray::array![];
    for array in vector_of_vectors.iter() {
        matrix = concatenate![Axis(0), matrix, Array::from(array.clone())];
    }
}
error[E0308]: mismatched types
  --> src/main.rs:12:18
   |
12 |         matrix = concatenate![Axis(0), matrix, Array::from(array.clone())];
   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an array with a fixed size of 2 elements, found one with 1 element
   |
   = note: expected struct `ArrayBase<ViewRepr<&bool>, Dim<[usize; 2]>>`
              found struct `ArrayBase<ViewRepr<&bool>, Dim<[usize; 1]>>`
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

Upvotes: 5

Views: 7111

Answers (1)

Jason
Jason

Reputation: 5545

See the documentation for ndarray:

Known row and column length

use ndarray::{Array2, Axis};

fn main() {
    #[rustfmt::skip]
    let rand_vec = vec![
        vec![true, false, true],
        vec![false, true, false],
    ];

    let mut arr = Array2::<bool>::default((2, 3));
    for (i, mut row) in arr.axis_iter_mut(Axis(0)).enumerate() {
        for (j, col) in row.iter_mut().enumerate() {
            *col = rand_vec[i][j];
        }
    }
}

Playground

Unknown row and column length

The documentation suggests to efficiently flatten the Vec using extend_from_slice and pass the flattened result to Array2::from_shape_vec:

use ndarray::Array2;

fn main() {
    // A Vec<Vec<bool>> with a random length of rows and columns
    let rand_vec = gen_rand_vec();

    let mut data = Vec::new();

    let ncols = rand_vec.first().map_or(0, |row| row.len());
    let mut nrows = 0;

    for i in 0..rand_vec.len() {
        data.extend_from_slice(&rand_vec[i]);
        nrows += 1;
    }

    let arr = Array2::from_shape_vec((nrows, ncols), data).unwrap();
}

Playground

Given the former input for rand_vec, both examples will give you:

[
  [true, false, true],
  [false, true, false],
]

Upvotes: 6

Related Questions