5

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)
0

1 Answer 1

6

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],
]
Sign up to request clarification or add additional context in comments.

3 Comments

the doc advice to use Array2::zeros if you shape is already known
Thank you so much! This works for me when the lengths are known at compile time. However in my despair last night I typoed the axis to 0 and not 1. So to all the other noobies change Axis 0 to 1 when adding columns instead of rows.
I still cant wrap my head around creating a matrix column-wise from dynamically sized data using from_shape_vec but there seems to be a pull request coming through adding try_append_row and try_append_column methods to 2D arrays https://github.com/rust-ndarray/ndarray/pull/932. So this will add some ergonomics in the future!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.