Julia:一种从数组数组中获取矩阵的快速而优雅的方法

Julia: A fast and elegant way to get a matrix from an array of arrays

有一个包含超过 10,000 对 Float64 值的数组数组。像这样:

v = [[rand(),rand()], ..., [rand(),rand()]]

我想得到一个包含两列的矩阵。可以用一个循环绕过所有的pair,看起来很麻烦,但是几分之一秒就给出了结果:

x = Vector{Float64}()
y = Vector{Float64}()
for i = 1:length(v)
    push!(x, v[i][1])
    push!(y, v[i][2])
end
w = hcat(x,y)

我在 this task 中找到的 permutedims(reshape(hcat(v...), (length(v[1]), length(v)))) 的解决方案看起来更优雅但完全挂起 Julia,需要重新启动会话。可能六年前是最优的,现在在大阵的情况下就不行了。有没有既紧凑又快速的解决方案?

我希望这对你来说足够简短有效:

 getindex.(v, [1 2])

如果你想要更简单的东西来消化:

[v[i][j] for i in 1:length(v), j in 1:2]

另外 hcat 解决方案可以写成:

permutedims(reshape(reduce(hcat, v), (length(v[1]), length(v))));

它不应该挂起你的 Julia(请确认 - 它对我有用)。

@Antonello:要理解为什么这有效,请考虑一个更简单的示例:

julia> string.(["a", "b", "c"], [1 2])
3×2 Matrix{String}:
 "a1"  "a2"
 "b1"  "b2"
 "c1"  "c2"

我正在广播一列 Vector ["a", "b", "c"] 和 1 行 Matrix [1 2]。重点是 [1 2] 是一个 Matrix。因此它使广播扩展行(由向量强制)和列(由 Matrix 强制)。要实现这种扩展,[1 2] 矩阵只有一行是至关重要的。现在清楚了吗?

您自己的示例非常接近于一个好的解决方案,但是通过创建两个不同的向量并重复使用 push! 做了一些不必要的工作。此解决方案类似,但更简单。它不像@BogumilKaminski 广播的 getindex 那样简洁,但速度更快:

function mat(v)
    M = Matrix{eltype(eltype(v))}(undef, length(v), 2)
    for i in eachindex(v)
        M[i, 1] = v[i][1]
        M[i, 2] = v[i][2]
    end
    return M
end

您可以在不损失性能的情况下进一步简化它,如下所示:

function mat_simpler(v)
    M = Matrix{eltype(eltype(v))}(undef, length(v), 2)
    for (i, x) in pairs(v)
        M[i, 1], M[i, 2] = x
    end
    return M
end

目前发布的各种解决方案的基准...

using BenchmarkTools
# Creating the vector
v = [[i, i+0.1] for i in 0.1:0.2:2000]

M1 = @btime vcat([[e[1] e[2]] for e in $v]...)
M2 = @btime getindex.($v, [1 2])
M3 = @btime [v[i][j] for i in 1:length($v), j in 1:2]
M4 = @btime permutedims(reshape(reduce(hcat, $v), (length($v[1]), length($v))))
M5 = @btime permutedims(reshape(hcat($v...), (length($v[1]), length($v))))

function original(v)
    x = Vector{Float64}()
    y = Vector{Float64}()
    for i = 1:length(v)
        push!(x, v[i][1])
        push!(y, v[i][2])
    end
    return hcat(x,y)
end
function mat(v)
    M = Matrix{eltype(eltype(v))}(undef, length(v), 2)
    for i in eachindex(v)
        M[i, 1] = v[i][1]
        M[i, 2] = v[i][2]
    end
    return M
end
function mat_simpler(v)
    M = Matrix{eltype(eltype(v))}(undef, length(v), 2)
    for (i, x) in pairs(v)
        M[i, 1], M[i, 2] = x
    end
    return M
end

M6 = @btime original($v)
M7 = @btime mat($v) 
M8 = @btime mat($v)

M1 == M2 == M3 == M4 == M5 == M6 == M7 == M8 # true

输出:

1.126 ms (10010 allocations: 1.53 MiB)       # M1
  54.161 μs (3 allocations: 156.42 KiB)      # M2
  809.000 μs (38983 allocations: 765.50 KiB) # M3
  98.935 μs (4 allocations: 312.66 KiB)      # M4
  244.696 μs (10 allocations: 469.23 KiB)    # M5
219.907 μs (30 allocations: 669.61 KiB)      # M6
34.311 μs (2 allocations: 156.33 KiB)        # M7
34.395 μs (2 allocations: 156.33 KiB)        # M8

请注意,基准测试代码中的美元符号只是为了强制 @btime 将向量视为局部变量。