在具有条件

时间:2016-10-06 15:56:04

标签: julia

我在某处读到循环而不是矢量化操作在Julia中表现更好。然而,我对数组/数据帧的R-like索引感到困惑,我不知道它是如何实现的。当阵列很大时,我也没有看到改进它的直接解决方案。

arr = zeros(Int64, (10,2))
out = zeros(Int64, (10,2))
arr[:,1] = [1 2 3 4 5 6 7 8 9 10]
arr[:,2] = [2 3 2 3 4 4 5 6 7 5]
for i in 1:10
    for j in 1:2
        x[i,j]=sum(arr[arr[:,2] .== i, j])
    end
end
x

这只是数组的演示,arr通常是一个几乎有~100K行的数组。 这很好用,但我想知道是否有更好的表现方式,我可以做到这一点。

2 个答案:

答案 0 :(得分:2)

Julia是专栏专业,因此您希望循环列,而不是跨行。

arr = zeros(Int64, (10,2))
out = similar(arr)
x   = similar(arr)
arr[:,1] = [1 2 3 4 5 6 7 8 9 10]
arr[:,2] = [2 3 2 3 4 4 5 6 7 5]
for j in 1:2, i in 1:10
    x[i,j]=sum(arr[arr[:,2] .== i, j])
end
x

请注意,每次内部迭代都会保持在同一列中,这样性能更高(请查看performance tips)。

最后,请注意arr[:,2]每次完成后都会创建第二列的副本。最好创建一个“视图”,即一个不复制数组的类型,只是创建一个看起来像向量的shell类型,但仍然指向与arr相同的值。这是通过view(arr,:,2)完成的。所以你可以做到

arr = zeros(Int64, (10,2))
out = similar(arr)
x   = similar(arr)
arr[:,1] = [1 2 3 4 5 6 7 8 9 10]
arr[:,2] = [2 3 2 3 4 4 5 6 7 5]
for j in 1:2, i in 1:10
    x[i,j]=sum(view(arr,view(arr,:,2) .== i, j))
end
x

当矩阵较大时,这只会更快。

答案 1 :(得分:2)

在我的电脑上,以下速度是克里斯'的速度的100倍。 view版本:

x = zeros(Int, 10, 2)
for j in 1:size(arr, 2), i in 1:size(arr, 1)
    x[arr[i, 2], j] += arr[i, j]
end

修改:因此,我将所有内容都包含在函数中并使用BenchmarkTools计时:

原始功能:

function accum0(arr)
    x = similar(arr)
    for i in 1:size(arr, 1)
        for j in 1:size(arr, 2)
            x[i,j]=sum(arr[arr[:,2] .== i, j])
        end
    end
    return x
end

克里斯'版本:

function accum1(arr)
    x = similar(arr)
    for j in 1:size(arr, 2), i in 1:size(arr, 1)
        x[i,j]=sum(view(arr,view(arr,:,2) .== i, j))
    end
    return x
end

我的版本:

function accum(a)
    x = zeros(eltype(a), size(a))
    for j in 1:size(a, 2), i in 1:size(a, 1)
        x[a[i, 2], j] += a[i, j]
    end
    return x
end

以下是具有更大矩阵的时间:

julia> using BenchmarkTools
julia> const A = hcat(collect(1:2*10^4), rand(1:1000, 2*10^4));

有些热身,然后:

julia> @benchmark accum0(A)
BenchmarkTools.Trial: 
  samples:          2
  evals/sample:     1
  time tolerance:   5.00%
  memory tolerance: 1.00%
  memory estimate:  6.26 gb
  allocs estimate:  1080002
  minimum time:     3.64 s (14.60% GC)
  median time:      3.83 s (17.94% GC)
  mean time:        3.83 s (17.94% GC)
  maximum time:     4.02 s (20.97% GC)

julia> @benchmark accum1(A)
BenchmarkTools.Trial: 
  samples:          2
  evals/sample:     1
  time tolerance:   5.00%
  memory tolerance: 1.00%
  memory estimate:  308.98 mb
  allocs estimate:  1005068
  minimum time:     2.69 s (3.81% GC)
  median time:      2.74 s (4.37% GC)
  mean time:        2.74 s (4.37% GC)
  maximum time:     2.79 s (4.90% GC)

julia> @benchmark accum(A)
BenchmarkTools.Trial: 
  samples:          10000
  evals/sample:     1
  time tolerance:   5.00%
  memory tolerance: 1.00%
  memory estimate:  312.61 kb
  allocs estimate:  3
  minimum time:     75.60 μs (0.00% GC)
  median time:      169.22 μs (0.00% GC)
  mean time:        210.90 μs (21.65% GC)
  maximum time:     108.19 ms (99.75% GC)

正如您所看到的,差异相当大(超过四个数量级)。使用view几乎没有收获,仍然有很多分配。对于稍微大一点的矩阵,accum0accum1都不会在合理的时间内返回。