# Converting a matrix into an array of arrays

Hi all

I have seen many examples of converting an array of arrays into a matrix, but I would like to go the other way. What I have cobbled together works, but may not be the most elegant or fast solution. I would appreciate any comments and builds.

function sliceMatrix(A)
m, n = size(A)
B = Array{Array{eltype(A), 1}, 1}(undef, m)

for i = 1:m
B[i] = A[i, :]
end
return B
end

Is there a way to pre-allocate the individual arrays inside B?

Thanks!

1 Like

I don’t think you gain much from preallocating in this situation, but here is one way where I only modified your functions a little bit:

julia> function slicematrix(A::AbstractMatrix{T}) where T
m, n = size(A)
B = Vector{T}[Vector{T}(undef, n) for _ in 1:m]
for i in 1:m
B[i] .= A[i, :]
end
return B
end
slicematrix (generic function with 1 method)

julia> slicematrix(rand(2,3))
2-element Array{Array{Float64,1},1}:
[0.686489, 0.016934, 0.638272]
[0.317802, 0.543907, 0.208549]

but it is probably better and simpler to just define it as:

function slicematrix(A::AbstractMatrix)
return [A[i, :] for i in 1:size(A,1)]
end

Note also that Julia’s Array is column major, so extracting columns (A[:, i]) is much faster than extracting rows (A[i, :]).

3 Likes

A one liner, likely not as efficient as the suggestion above, is
mapslices(x->[x], randn(5,5), dims=2)[:]

3 Likes

Thank you all! The suggestions are much appreciated.

While trying this myself, I found a faster solution than the simple suggestion above.

Iterate over the columns with eachcol or eachrow:

A = rand(5,10000)
@btime [c[:] for c in eachcol(A)]
@btime [A[:,c] for c in 1:size(A,2)]

julia>
382.300 μs (20005 allocations: 1.75 MiB)
2.788 ms (38985 allocations: 1.74 MiB)
1 Like

Note that your benchmarking produced false inferences due to accessing global variables. If you interpolate the variables into the expressions (using \$). The results are much more similar.
I also include an even faster version, if you can work with static vectors:

using StaticArrays
A = rand(5,10000)
@btime [c[:] for c in eachcol(\$A)]
@btime [\$A[:,c] for c in 1:size(\$A,2)]
@btime vec(reinterpret(SVector{size(\$A,1),eltype(\$A)},\$A))

506.052 μs (20005 allocations: 1.75 MiB)
440.997 μs (10004 allocations: 1.30 MiB)
1.542 μs (11 allocations: 704 bytes)

5 Likes

I think @MatthijsCox approx with eachcol is in fact faster (but still slower than StaticArrays), the problem is how columns are collected.

A = rand(5,10000);
@btime collect(eachcol(\$A));
@btime [c[:] for c in eachcol(\$A)];
@btime [\$A[:,c] for c in 1:size(\$A,2)];
[A[:,c] for c in 1:size(A,2)] == collect(eachcol(A))

> julia
96.849 μs (10004 allocations: 547.00 KiB)
481.677 μs (20005 allocations: 1.75 MiB)
413.083 μs (10004 allocations: 1.30 MiB)
true

Note that the latter two create copies, while the first one returns views:

julia> @btime [c[:] for c in eachcol(\$A)];
535.007 μs (20005 allocations: 1.75 MiB)

julia> @btime [c for c in eachcol(\$A)];
80.548 μs (10005 allocations: 547.02 KiB)
4 Likes