Creating a matrix from array of arrays without changing order of dimensions

As far as I know the way in julia to create a 2D-Matrix from an Array of 1D-arrays is to use hcat(a...). My problem now is that it seems that julia’s column-major style of arrays changes the order of dimensions.

a = [[1, 2], [3, 4]]
# now a[1][2] is 2
a = hcat(a...)
# now a[1,2] is 3

As mentioned in the code above, I realized that this code changed the order of my dimensions. So I asked myself whether there is a way of creating this matrix, without needing to transpose it afterwards.
Thanks in advance!

1 Like
julia> vcat(a'...)
2×2 Array{Int64,2}:
 1  2
 3  4
2 Likes

Thank you! Is there also a convenient way to do it if a consists of something else than numbers, for example Strings or Characters?

julia> vcat(permutedims.(a)...)
2×2 Array{Char,2}:
 'a'  'b'
 'c'  'd'
1 Like

Thank you! Really helped!

vcat(a'...) is quite inefficient for what it’s worth. The problem is that the code generated depends on the length of a', but that information isn’t known until runtime (rather than compiletime).

Normally, I’d recommend a reduce, but unfortunately we currently don’t have an optimized code-path for reduce(vcat, a') (unlike redunce(hcat, a)'):

julia> let a = [rand(10) for _ ∈ 1:100]
           o1 = @btime vcat($a'...)
           o2 = @btime reduce(vcat, $a')
           o3 = @btime reduce(hcat, $a)'
           o1 == o2 == o3
       end
  14.990 μs (509 allocations: 23.62 KiB)
  78.049 μs (393 allocations: 416.27 KiB)
  1.131 μs (1 allocation: 7.94 KiB)
true

Hence, if the OP doesn’t want an adjoint or permuted matrix as an output and care about performance, you can get a big benefit by rolling up your sleeves and writing your own function:

julia> function vec_o_vec_to_mat(a::Vector{Vector{T}}) where {T}
           n, m = length(a), length(a[1])
           @assert all(ai -> length(ai) == m, a)
           A = Array{T}(undef, n, m)
           @inbounds for i ∈ 1:n
               A[i, :] = a[i]
           end
           A
       end;

julia> let a = [rand(10) for _ ∈ 1:100]
           o1 = @btime vcat($a'...)
           o2 = @btime reduce(vcat, $a')
           o3 = @btime reduce(hcat, $a)'
           o4 = @btime vec_o_vec_to_mat($a)
           o1 == o2 == o3 == o4
       end
  15.589 μs (509 allocations: 23.62 KiB)
  74.820 μs (393 allocations: 416.27 KiB)
  1.131 μs (1 allocation: 7.94 KiB)
  1.941 μs (1 allocation: 7.94 KiB)
true

As you can see, this is almost as fast as reduce(hcat, a)', but produces a Matrix, rather than an Adjoint{<:Matrix}. The reason it’s slower is that since julia is column-major ordered, the memory layout of your vector or vectors is inefficient for building up a matrix (this is why it’s probaby a good idea to use ' or permutedims if you can).

1 Like