我在某处读到循环而不是矢量化操作在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行的数组。 这很好用,但我想知道是否有更好的表现方式,我可以做到这一点。
答案 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
几乎没有收获,仍然有很多分配。对于稍微大一点的矩阵,accum0
和accum1
都不会在合理的时间内返回。