Convert a tuple vector into a matrix in Julia

Viewed 504

Let's suppose we have a function, that gives us following:

julia> ExampleFunction(Number1, Number2)
5-element Vector{Tuple{Int64, Int64}}:
 (2, 2)
 (2, 3)
 (3, 3)
 (3, 2)
 (4, 2)

I would like to convert Vector{Tuple{Int64, Int64}} into a matrix, or in my case I would like to convert it into a 5x2 Matrix.

2 Answers

The fastest code would be a non-copying one. In example from the other post this would reduce times from milliseconds to nanoseconds:

julia> xs
1000000-element Vector{Tuple{Int64, Int64}}:
 (5, 1)
 (4, 3)
 ⋮
 (1, 4)
 (9, 2)

julia> @btime reshape(reinterpret(Int, $xs), (2,:))'
  10.611 ns (0 allocations: 0 bytes)
1000000×2 adjoint(reshape(reinterpret(Int64, ::Vector{Tuple{Int64, Int64}}), 2, 1000000)) with eltype Int64:
  5   1
  4   3
  ⋮
  1   4
  9   2

And for the copying code the fastest is going to be:

function convert_to_tuple4(x)
   out = Matrix{eltype(x[1])}(undef, length(x), length(x[1]))
   for i ∈ 1:length(x)
         @inbounds @simd for j ∈ 1:length(x[1])
             out[i, j] = x[i][j]
         end
   end
   out
end

Benchmarks:

julia> @btime convert_to_tuple3($xs);
  3.488 ms (2 allocations: 15.26 MiB)

julia> @btime convert_to_tuple4($xs);
  2.932 ms (2 allocations: 15.26 MiB)

It's not entirely clear from your question how the 5-element vector of tuples of length 2, which has 10 elements in total, should be converted to a 3x2 matrix which holds 6 elements, but assuming that you meant 5x2 here's one way of doing it:

julia> x = [(1,2), (2, 3), (3, 4)]
3-element Vector{Tuple{Int64, Int64}}:
 (1, 2)
 (2, 3)
 (3, 4)

julia> hcat(first.(x), last.(x))
3×2 Matrix{Int64}:
 1  2
 2  3
 3  4

EDIT: As Phips mentioned an alternative below, here's a quick benchmark on Julia 1.7beta3, Windows 10 - I've thrown in a loop version as well, as it always makes sense to try a straightforward loop in Julia:

julia> convert_to_tuple1(x) = hcat(first.(x), last.(x))
convert_to_tuple1 (generic function with 1 method)

julia> convert_to_tuple2(x) = PermutedDimsArray(reshape(foldl(append!, x, init = Int[]), 2, :), (2, 1))
convert_to_tuple2 (generic function with 1 method)

julia> function convert_to_tuple3(x)
           out = Matrix{eltype(x[1])}(undef, length(x), length(x[1]))
           for i ∈ 1:length(x)
               for j ∈ 1:length(x[1])
                   out[i, j] = x[i][j]
               end
           end
           out
       end
convert_to_tuple3 (generic function with 1 method)

julia> xs = [(rand(1:10), rand(1:10)) for _ ∈ 1:1_000_000];

julia> using BenchmarkTools

julia> @btime convert_to_tuple1($xs);
  15.789 ms (6 allocations: 30.52 MiB)

julia> @btime convert_to_tuple2($xs);
  22.067 ms (21 allocations: 18.91 MiB)

julia> @btime convert_to_tuple3($xs);
  7.286 ms (2 allocations: 15.26 MiB)

(further edited to add $ for interpolation of xs into the benchmark)

Related