【问题标题】:Julia: A fast and elegant way to get a matrix from an array of arraysJulia:一种从数组数组中获取矩阵的快速而优雅的方法
【发布时间】:2021-08-07 00:25:08
【问题描述】:

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

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

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

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,需要重新启动会话。也许六年前它是最佳的,但现在它不适用于大型阵列。有没有既紧凑又快速的解决方案?

【问题讨论】:

  • 我不明白为什么您的循环示例会创建两个向量,xy。为什么不直接创建一个矩阵,然后将值直接写入其中呢?看起来更直接?

标签: arrays julia reshape arrayofarrays


【解决方案1】:

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

 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"] 和一排 Matrix [1 2]。关键是[1 2]Matrix。因此,它使广播同时扩展行(由向量强制)和列(由Matrix 强制)。要使这种扩展发生,[1 2] 矩阵恰好只有一行是至关重要的。现在清楚了吗?

【讨论】:

  • 广播的getindex有什么作用?我本来希望一个向量作为输出,因为它是一个广播操作..
  • 我会在答案中解释。
  • reshape 有什么作用吗? permutedims(reduce(hcat, v)) 应该可以工作,(对于实数数组)reduce(vcat, v').
  • 你在这两种情况下都是对的 - 我只是专注于解决飞溅问题 - 没有检查代码。
【解决方案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

【讨论】:

    【解决方案3】:

    迄今为止发布的各种解决方案的基准...

    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 将向量视为局部变量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-11
      • 2021-10-18
      • 2016-02-11
      • 1970-01-01
      • 2023-04-05
      • 2014-07-10
      • 1970-01-01
      相关资源
      最近更新 更多