Python 计算numpy-ndarray序列的外积

Python 计算numpy-ndarray序列的外积,python,performance,numpy,matrix,scientific-computing,Python,Performance,Numpy,Matrix,Scientific Computing,我有一个三维点列表p存储在一个带有形状(N,3)的数据阵列中。我想计算每个3d点自身的外积: N = int(1e4) p = np.random.random((N, 3)) result = np.zeros((N, 3, 3)) for i in range(N): result[i, :, :] = np.outer(p[i, :], p[i, :]) 有没有一种方法可以在不使用任何python级循环的情况下计算这个外部产品?问题是np.outer不支持类似于轴的任何参数 您至

我有一个三维点列表p存储在一个带有形状(N,3)的数据阵列中。我想计算每个3d点自身的外积:

N = int(1e4)
p = np.random.random((N, 3))
result = np.zeros((N, 3, 3))
for i in range(N):
    result[i, :, :] = np.outer(p[i, :], p[i, :])

有没有一种方法可以在不使用任何python级循环的情况下计算这个外部产品?问题是
np.outer
不支持类似于
轴的任何参数

您至少可以使用
沿轴应用

result = np.apply_along_axis(lambda point: np.outer(point, point), 1, p)
然而,令人惊讶的是,这实际上比您的方法慢:

In [ ]: %%timeit N = int(1e4); p = np.random.random((N, 3))
   ...: result = np.apply_along_axis(lambda point: np.outer(point, point), 1, p)
61.5 ms ± 1.84 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [ ]: %%timeit N = int(1e4); p = np.random.random((N, 3))
   ...: result = np.zeros((N, 3, 3))
   ...: for i in range(N):
   ...:     result[i, :, :] = np.outer(p[i, :], p[i, :])
46 ms ± 709 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

您可以使用广播:

p[..., None] * p[:, None, :]

此语法在第一个术语的末尾(即Nx3x1)和第二个术语的中间(即Nx1x3)插入一个轴。然后广播这些内容并产生Nx3x3结果。

比我之前的解决方案更好的解决方案是使用
np。einsum

np.einsum('...i,...j', p, p)
这比广播方式更快:

In [ ]: %timeit p[..., None] * p[:, None, :]
514 µs ± 4.23 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [ ]: %timeit np.einsum('...i,...j', p, p)
169 µs ± 1.75 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
至于它是如何工作的,我不太清楚,我只是在
einsum
上乱搞,直到我得到了我想要的答案:

In [ ]: np.all(np.einsum('...i,...j', p, p) == p[..., None] * p[:, None, :])
Out[ ]: True

这只是隐藏了python级别的循环。本质上,是的。事实上,它甚至更慢。请参阅我的编辑。是的,我可以通过独立的计时测试进行确认。我用列表理解做了同样的测试,结果是一样的。这些循环是在python级别完成的。哇,很好的解决方案。谢谢你能帮我找出为什么我的解决方案有效吗?它甚至更快,但我不理解在
einsum
中广播的概念。点告诉einsum您只对最右边的轴感兴趣,希望剩下的轴不受影响。i和j表示最后的轴;它们是不同的,这告诉einsum在输出中为它们创建单独的轴,因此简言之:无或省略号->正常广播;两个参数中的同一字母->沿此轴减少;字母仅出现在一个术语->中的独立轴output@PaulPanzer非常感谢。有没有办法不使用圆点?应该有,因为维度的数量是固定的,对吗?@Sebastian您可以使用
'ij,ik->ijk'
。你说维度的数量是固定的是什么意思?似乎每当问“我能怎么做?”总是有一个涉及
einsum
的答案,这个答案比任何其他方法都要快。我永远都不能很好地理解文档,但就我而言,它是一个神奇的盒子,可以解决所有的数组操作和操作例程,只要别人告诉你是怎么回事。@AlexanderReynolds我也不知道它是如何工作的。我所知道的是,如果只对数组的一部分进行加法或乘法,
einsum
将完成其他函数所做不到的任何事情。值得深入研究
einsum
文档,尤其是如果您对爱因斯坦求和符号有任何先验知识,尽管通常使用
@
np时,Divakar会带来更快的结果。点
爱因斯坦符号的基本上下文是对重复的索引进行多重计算,并对丢失的索引进行求和。因此或示例
'ij,jk->ij'
j
维度相乘,然后在
k
维度上求和-而
'ij,jk->ik`将
j
维度相乘并求和。@DanielF尽管经常使用@np或np.dot会带来更快的结果。。。或
tensordot