Julia中矩阵之间的逐行操作



我正在尝试将以下Python代码(从SMT GEKPLS)等效翻译为Julia:

def differences(X, Y):
D = X[:, np.newaxis, :] - Y[np.newaxis, :, :]
return D.reshape((-1, X.shape[1]))

那么,给定这样的输入:

X = np.array([[1.0,1.0,1.0], [2.0,2.0,2.0]])
Y = np.array([[1.0,2.0,3.0], [4.0,5.0,6.0], [7.0,8.0,9.0]])
diff = differences(X,Y)

我们得到的输出(diff)如下所示:

[[ 0. -1. -2.]
[-3. -4. -5.]
[-6. -7. -8.]
[ 1.  0. -1.]
[-2. -3. -4.]
[-5. -6. -7.]]

用Julia代码做这件事的有效方法是什么?我期望X和Y输入矩阵相当大。

经过一番思考,我想到了这个函数:

function differences(X, Y)
Rx = repeat(X, inner=(size(Y, 1), 1))
Ry = repeat(Y, size(X, 1))
Rx - Ry
end

我希望我对你有所帮助。

这是一个避免repeat的版本,这会产生不必要的数据重复:

function diffs_row(X, Y)
N = size(X, 2)
return reshape(reshape(X', 1, N, :) .- Y', N, :)'
end

所有伴随'的原因是在Julia中按行操作并不是很自然。Julia数组是列主要的,所以reshape将检索数据列明智。如果你决定改变数据的方向,你可以写

function diffs_col(X, Y)
N = size(X, 1)
return reshape(reshape(X, N, 1, :) .- Y, N, :)
end

在将numpy代码翻译成Julia时经常会看到这种情况。Numpy本身是行主要的,所以翻译起来有点尴尬。在许多情况下,您应该考虑将数据布局更改为列为主。

这可能比其他选择更快,同时仍然易于理解。

[x .- y for x ∈ X for y ∈ Y]
6-element Vector{Vector{Float64}}:
[0.0, -1.0, -2.0]
[-3.0, -4.0, -5.0]
[-6.0, -7.0, -8.0]
[1.0, 0.0, -1.0]
[-2.0, -3.0, -4.0]
[-5.0, -6.0, -7.0]

我不喜欢numpy的一件事是,必须准确地记住每个函数与输入参数的组合。在Julia中,传统的循环可以作为大多数算法的有效替代。

附录:如上所述,如果使用Vector{Vector{Float64}}不是问题,则可能是最快的解决方案。如果是,这里有另一个解决方案,输出Matrix{Float64},同时也很快。

function diffr(X,Y) 
i, l, m, n = 0, length(first(X)), length(X), length(Y)
Z = Matrix{Float64}(undef, m*n, l)
for x in X, y in Y
Z[i+=1,:] .= x .- y
end
Z
end

这是在我的电脑上发布的所有解决方案的性能比较。

@btime [x.-y for x∈$X for y∈$Y] # 312.245 ns (9  allocations: 656 bytes)
@btime diffr($X, $Y)            #  73.868 ns (1  allocation:  208 bytes)
@btime differences($X, $Y)      # 439.000 ns (12 allocations: 896 bytes)
@btime diffs_row($X, $Y)        # 463.131 ns (11 allocations: 784 bytes)

最新更新