How to convert Vector{Array{T,N}} to Array{T,N+1}

Is there any way (without using third party packages) to convert a vector of multi-dimensional array to multidimensional array, such as convert [T_1,T_2,...,T_q] (each T_i is a multi-dimensional array of type Array{T,3}, with size r×s×t) to multi-dimensional array M (of type Array{T,4}) with size q×r×s×t and M[i,:,:,:,] is identical to T_i

Thanks for the help!

Maybe this:

julia> vec_of_arr = fill(rand(3,4,5),6);

julia> arr = cat(vec_of_arr...,dims=4);

julia> size(arr)
(3, 4, 5, 6)

You can use permutedims(arr, (4,1,2,3)) to move the last axis to first position in indexing.

Check the documentation for cat and permutedims for more information.

1 Like

Thanks for this awesome solution, it works like a charm!

I am still quite confused about the design of multi-dimension array in Julia. Isn’t that quite intuitive to have vector of multi-dimensional arrays get raised to higher dimensional array with the first dimension indexing those original arrays? but cat seems do not support “create” a new dimension before existing dimensions.

1 Like

Kinda depends on how I interpret your question.

Vector of nD Arrays is basically a list of pointers to arrays, so iterating through it inherently requires following a pointer. This is different from an (n+1)D Array, which can be stored densely in a single contiguous piece of memory. This has fairly significant performance implications, thus I am happy that Julia makes interacting with them explicitly different.

If you do want the more convenient indexing, you can use tools like RecursiveArrayTools.jl which simplify the indexing for you.

Concerning why cat added a new index at the end instead of at the beginning, it is because I used dims=4. By the way, there is a literal version of cat that is equivalent: [vec_of_arr...;;;;] where the number of semicolons specifies the dimension along which you want to concatenate. Both cat and the literal inherit this style from how array literals work in julia: each semicolon adds a new dimension (at the back of the list of indices).

Lastly, the order of indices, I claim, is consistent with the fact that Julia has Fortran style, not C style matrices. I.e. the first index is the fastest growing one, not the last index. E.g. see this example:

julia> a = [1 3; 2 4] # or maybe a = [1; 2;; 3; 4] to make it more explicit
2×2 Matrix{Int64}:
 1  3
 2  4

julia> a[1], a[2], a[3], a[4] # single-index lookup gives us the contiguous in-memory array
(1, 2, 3, 4)

julia> a[1,1], a[1,2] # modifying the last index causes discontinuous jumps
(1, 3)

julia> a[1,1], a[2,1] # modifying the first index gives neighbors
(1, 2)

Thus, the first index should refer to contiguous neighbors-in-memory, while the last index should refer to big jumps in memory, which hopefully makes what you are observing make sense.

Also, the arrays inside a vector of arrays aren’t necessarily the same size, so they cannot even be automatically ‘promoted’ to N+1 dimensions, in general.

1 Like

BTW, this is precisely what stack(vec_of_arr; dims=1). For which you’ll need using Compat until Julia 1.9 emerges. This should be faster, especially if there are many arrays.

julia> vec_of_arr = fill(rand(3,4,5),6);

julia> arr = @btime permutedims(cat($vec_of_arr...; dims=4), (4,1,2,3));
  min 4.607 μs, mean 5.812 μs (77 allocations, 9.00 KiB)

julia> arr ≈  @btime stack($vec_of_arr; dims=1)
  min 231.739 ns, mean 602.204 ns (2 allocations, 2.98 KiB)
true

Without dims it will put the vector’s index last, matching cat(vec_of_arr...; dims=4). As mentioned that’s the natural order in memory, i.e. numbers which are at neighbouring addresses in each array are still neighbours in the final arr.

1 Like

I was curious what is the history of this newly added stack function. I suspect this would be interesting read for multiple people here:

The implementation: Add `stack(array_of_arrays)` by mcabbott · Pull Request #43334 · JuliaLang/julia · GitHub
The rationale: https://github.com/JuliaLang/julia/issues/21672

1 Like