双python for循环的numpy矢量化
V是(n,p)numpy数组,通常维数为n~10,p~20000 我现在的代码看起来像双python for循环的numpy矢量化,python,numpy,linear-algebra,Python,Numpy,Linear Algebra,V是(n,p)numpy数组,通常维数为n~10,p~20000 我现在的代码看起来像 A = np.zeros(p) for i in xrange(n): for j in xrange(i+1): A += F[i,j] * V[i,:] * V[j,:] 我该如何重写它以避免双重python for循环?这方面的困难之处在于,您只想使用j获取元素的总和,而Isaac的答案似乎很有希望,因为它删除了这两个嵌套的for循环,您必须创建一个中间数组M,它是原始V数组大
A = np.zeros(p)
for i in xrange(n):
for j in xrange(i+1):
A += F[i,j] * V[i,:] * V[j,:]
我该如何重写它以避免双重python for循环?这方面的困难之处在于,您只想使用
j获取元素的总和,而Isaac的答案似乎很有希望,因为它删除了这两个嵌套的for循环,您必须创建一个中间数组M
,它是原始V
数组大小的n
倍。Python for循环并不便宜,但内存访问也不是免费的:
n = 10
p = 20000
V = np.random.rand(n, p)
F = np.random.rand(n, n)
def op_code(V, F):
n, p = V.shape
A = np.zeros(p)
for i in xrange(n):
for j in xrange(i+1):
A += F[i,j] * V[i,:] * V[j,:]
return A
def isaac_code(V, F):
n, p = V.shape
F = F.copy()
F[np.triu_indices(n, 1)] = 0
M = (V.reshape(n, 1, p) * V.reshape(1, n, p)) * F.reshape(n, n, 1)
return M.sum((0, 1))
如果您现在两个都参加试驾:
In [20]: np.allclose(isaac_code(V, F), op_code(V, F))
Out[20]: True
In [21]: %timeit op_code(V, F)
100 loops, best of 3: 3.18 ms per loop
In [22]: %timeit isaac_code(V, F)
10 loops, best of 3: 24.3 ms per loop
因此,删除for循环会使您的速度降低8倍。不是一件很好的事情。。。在这一点上,您甚至可能要考虑一个大约3ms来评估的函数是否需要进一步优化。如果您这样做了,使用np.einsum
,可以有一个小小的改进:
def einsum_code(V, F):
n, p = V.shape
F = F.copy()
F[np.triu_indices(n, 1)] = 0
return np.einsum('ij,ik,jk->k', F, V, V)
现在:
In [23]: np.allclose(einsum_code(V, F), op_code(V, F))
Out[23]: True
In [24]: %timeit einsum_code(V, F)
100 loops, best of 3: 2.53 ms per loop
所以这大约是20%的速度,引入的代码可能不像for循环那样可读。我想说不值得…这个表达式可以写成
因此,您可以使用np.newaxis
-构造如下求和:
na = np.newaxis
X = (np.tri(n)*F)[:,:,na]*V[:,na,:]*V[na,:,:]
X.sum(axis=1).sum(axis=0)
这里构建了一个3D数组X[i,j,p]
,然后将前两个轴相加,形成一个1D数组a[p]
。此外,F
与三角形矩阵相乘,以根据问题限制求和 F
的形状是什么?是代码< >(n,n)<代码>还是<代码>(n,n,p)< /代码>?您是否考虑编写C中的循环(因为它们很简单),并且使用Spypy.WeaveBLITZ?我宁愿避免Cython或Weav.Belz暂时,如果一些Python代码将“足够好”,那么<代码> F[NP.TruuthCalpand(n,1)]=0 < /Cord>?这样的话,F
(偏移1)的上半部分是零,不会对求和产生影响。而且,a.sum(0)。sum(0)
可以写成np.sum(a,(0,1))
或a.sum((0,1))
,虽然速度不快,但我觉得它更可读。正如我所说,此代码确实不是矢量化的好候选。不知道einsum
;非常整洁!是的。当整个过程有点麻烦时,矢量化代码的一个常见的启发式方法是在长轴上矢量化,在短轴上循环,这几乎可以让您从头痛的一小部分中获得所有好处。但是在这里,这就是OP已经做过的!谢谢,在使用一系列输入(n=5-30&p=100-20000)对每个输入进行测试之后,我最终使用了einsum方法,我不知道它在那里。
In [23]: np.allclose(einsum_code(V, F), op_code(V, F))
Out[23]: True
In [24]: %timeit einsum_code(V, F)
100 loops, best of 3: 2.53 ms per loop
na = np.newaxis
X = (np.tri(n)*F)[:,:,na]*V[:,na,:]*V[na,:,:]
X.sum(axis=1).sum(axis=0)