How to turn an array of arrays into a matrix in Julia?

Viewed 194

I want to turn a array of arrays into a matrix. To illustrate; let the array of arrays be:

[ [1,2,3], [4,5,6], [7,8,9]]

I would like to turn this into the 3x3 matrix:

[1 2 3
 4 5 6
 7 8 9]

How would you do this in Julia?

3 Answers

There are several ways of doing this. For instance, something along the lines of vcat(transpose.(a)...) will work as a one-liner

julia> a = [[1,2,3], [4,5,6], [7,8,9]]
3-element Vector{Vector{Int64}}:
 [1, 2, 3]
 [4, 5, 6]
 [7, 8, 9]

julia> vcat(transpose.(a)...)
3×3 Matrix{Int64}:
 1  2  3
 4  5  6
 7  8  9

though note that

  1. Since your inner arrays are column-vectors as written, you need to transpose them all before you can vertically concatenate (aka vcat) them (either that or horizontally concatenate and then transpose the whole result after, i.e., transpose(hcat(a...))), and

  2. The splatting operator ... which makes this one-liner work will not be very efficient when applied to Arrays in general, and especially not when applied to larger arrays-of-arrays.

Performance-wise for larger arrays-of-arrays, it will likely actually be hard to beat preallocating a result of the right size and then simply filling with a loop, e.g.

result = similar(first(a), length(a), length(first(a)))
for i=1:length(a)
    result[i,:] = a[i] # Aside: `=` is actually slightly faster than `.=` here, though either will have the same practical result in this case
end

Some quick benchmarks for reference:

julia> using BenchmarkTools

julia> @benchmark vcat(transpose.($a)...)
BechmarkTools.Trial: 10000 samples with 405 evaluations.
 Range (min … max):  241.289 ns …   3.994 μs  ┊ GC (min … max): 0.00% … 92.59%
 Time  (median):     262.836 ns               ┊ GC (median):    0.00%
 Time  (mean ± σ):   289.105 ns ± 125.940 ns  ┊ GC (mean ± σ):  2.06% ±  4.61%

  ▁▆▇█▇▆▅▅▅▄▄▄▄▃▂▂▂▃▃▂▂▁▁▁▂▄▃▁▁ ▁                             ▁ ▂
  ████████████████████████████████▇▆▅▆▆▄▆▆▆▄▄▃▅▅▃▄▆▄▁▃▃▃▅▄▁▃▅██ █
  241 ns        Histogram: log(frequency) by time        534 ns <

 Memory estimate: 320 bytes, allocs estimate: 5.

julia> @benchmark for i=1:length($a)
           $result[i,:] = $a[i]
       end
BechmarkTools.Trial: 10000 samples with 993 evaluations.
 Range (min … max):  33.966 ns … 124.918 ns  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     36.710 ns               ┊ GC (median):    0.00%
 Time  (mean ± σ):   39.795 ns ±   7.566 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

  ▄▄██▄▅▃ ▅▃ ▄▁▂ ▂▁▂▅▂▁    ▄▂▁                                 ▂
  ██████████████▇██████▆█▇▆███▆▇███▇▆▆▅▆▅▅▄▄▅▄▆▆▆▄▁▃▄▁▃▄▅▅▃▁▄█ █
  34 ns         Histogram: log(frequency) by time      77.7 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

In general, filling column-by-column (if possible) will be faster than filling row-by-row as we have done here, since Julia is column-major.

Expanding on @cbk's answer, another (slightly more efficient) one-liner is

julia> transpose(reduce(hcat, a))
3×3 transpose(::Matrix{Int64}) with eltype Int64:
 1  2  3
 4  5  6
 7  8  9
[1 2 3; 4 5 6; 7 8 9]

# or

reshape(1:9, 3, 3)' # remember that ' makes the transpose of a Matrix

Related