矢量化/广播具有不同尺寸的numpy阵列的Dot产品

时间:2017-11-06 13:29:44

标签: python numpy

问题:

我想计算一大组数据的点积。我能够在嵌套的for循环中执行此操作,但这太慢了。 这是一个小例子:

import numpy as np

points = np.array([[0.5, 2, 3, 5.5, 8, 11], [1, 2, -1.5, 0.5, 4, 5]])
lines = np.array([[0, 2, 4, 6, 10, 10, 0, 0], [0, 0, 0, 0, 0, 4, 4, 0]])
x1 = lines[0][0:-1]
y1 = lines[1][0:-1]
L1 = np.asarray([x1, y1])

# calculate the relative length of the projection
# of each point onto each line
a = np.diff(lines)
b = points[:,:,None] - L1[:,None,:]
print(a.shape)
print(b.shape)

[rows, cols, pages] = np.shape(b)
Z = np.zeros((cols, pages))
for k in range(cols):
    for l in range(pages):
        Z[k][l] = a[0][l]*b[0][k][l] + a[1][l]*b[1][k][l]

N = np.linalg.norm(a, axis=0)**2
relativeProjectionLength = np.squeeze(np.asarray(Z/N))

在此示例中,a和b的前两个维度表示点积所需的x坐标和y坐标。 a的形状是(2,7),b的形状是(2,6,7)。由于点积减小了第一维,我希望结果是形状(6,7)。如何在没有慢速循环的情况下计算出来?

我尝试了什么:

我认为numpy.dot可以正常播放,但是我无法正确设置尺寸。

a = a[:, None, :]
Z = np.dot(a,b)

这会给我以下错误:

  

未对齐的形状(2,1,7)和(2,6,7):7(暗淡2)!= 6(暗淡1)

2 个答案:

答案 0 :(得分:2)

您可以使用np.einsum -

np.einsum('ij,ikj->kj',a,b)

说明:

  • 保持两个输入的最后一个轴对齐。

  • 总结 - 减少第一个。

  • 让其余的留下来,这是b的第二个轴。

Usual rules on whether to use einsum or stick to a loopy-dot based method apply here

答案 1 :(得分:1)

numpy.dot不会减少第一个维度。来自文档:

  

对于N维,它是 a 的最后一个轴上的和积,而 b 的倒数第二个:

dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])

这正是错误告诉你的:它试图将第一个向量中的轴2与第二个向量中的轴1匹配。

您可以使用numpy.rollaxis或更好numpy.moveaxis来解决此问题。不是a = a[:, None, :],而是

a = np.movesxis(a, 0, -1)
b = np.moveaxis(b, 0, -2)
Z = np.dot(a, b)

更好的是,您可以构建阵列以预先确定正确的形状。例如,转置lines并执行a = np.diff(lines, axis=0)