Numpy:将矩阵的每一行添加到矩阵中(一次一个),然后在每个新矩阵中找到每行的min。希望加快代码速度

时间:2017-05-12 22:19:30

标签: python performance numpy matrix

我将矩阵的每一行添加到矩阵中,然后计算新矩阵中每行的最小值。

我目前来自python的代码是一个测试用例:

# Compute distances to all other nodes using landmarks 
distToLM = np.array([[1,2,3],[4,5,6],[7,8,9]])
m = len(distToLM)
count = 1
dist = np.zeros((m,m))
for i in range(m):
    findMin = distToLM[i,:] + distToLM.take(range(count,m),axis=0) 
    dist[i,count:]=np.min(findMin,axis = 1)
    count = count + 1

注意:我每次都在切割矩阵,因为我只需要矩阵的上三角值

所以第一次迭代会将[1,2,3]添加到[4,5,6]和[7,8,9]来制作一个矩阵:

[5,7,9]

[8,10,12]

从这里我想要每行的最小值,所以5和8。

接下来的迭代我会[4,5,6]将它添加到它下面的所有行,即[7,8,9]并取每行的最小值。

此代码相当慢,对于4000x4000矩阵大约需要3秒。

我也尝试过一个Cython版本,由于严重依赖调用n个函数VS在C中执行主代码,所以速度没有太大提升:

DTYPE=np.int

ctypedef np.int_t DTYPE_t
@cython.boundscheck(False)
@cython.wraparound(False)
def findDist(np.ndarray[DTYPE_t,ndim=2] distToLM):
    cdef int m = distToLM.shape[0]
    count = 1
    cdef np.ndarray[DTYPE_t, ndim=2] dist = np.zeros((m,m),dtype=DTYPE)
    cdef np.ndarray[DTYPE_t, ndim=2] findMin
    for i in range(m):
        findMin = distToLM[i,:] + distToLM.take(range(count,m),axis=0) 
        dist[i,count:]=np.min(findMin,axis = 1)
        count = count + 1
    return dist

我认为如果有某种方法可以对它进行矢量化,那就会快得多。

我愿意接受任何建议。

1 个答案:

答案 0 :(得分:0)

更改它有助于我更好地可视化操作(我不多使用take):

distToLM = np.array([[1,2,3],[4,5,6],[7,8,9]])
m = distToLM.shape[0]
dist = np.zeros((m,m), distToLM.dtype)
for i in range(m):
    findMin = distToLM[i,:] + distToLM[i+1:,:] 
    dist[i, i+1:] = np.min(findMin,axis = 1)

事实上,双重迭代甚至是明确的:

distToLM = np.array([[1,2,3],[4,5,6],[7,8,9]])
m = distToLM.shape[0]
dist = np.zeros((m,m), distToLM.dtype)
for i in range(m):
    for j in range(i+1,m):
        dist[i,j] = np.min(distToLM[i,:] + distToLM[j,:])

这揭示了代码中隐藏的2维中的对称性。它并不快,但使用Cython内存视图更容易实现。

这种对称性也表明我可以在这些行上执行'外部'和:

In [512]: np.min(distToLM[:,None,:]+distToLM[None,:,:],axis=-1)
Out[512]: 
array([[ 2,  5,  8],
       [ 5,  8, 11],
       [ 8, 11, 14]])

上三分是所需的dist。

In [518]: np.triu(_,k=1)
Out[518]: 
array([[ 0,  5,  8],
       [ 0,  0, 11],
       [ 0,  0,  0]])

这比迭代方法计算的值更多,但可以更快。不幸的是,对于您的大问题,中间大小(4000,4000,4000)数组可能对于内存来说太大了。

我可以事先选择triu指数:

In [530]: I,J=np.triu_indices(3,1)
In [531]: I,J
Out[531]: (array([0, 0, 1], dtype=int32), array([1, 2, 2], dtype=int32))
In [532]: np.min(distToLM[I,:]+distToLM[J,:],axis=1)
Out[532]: array([ 5,  8, 11])

我对大阵列的表现没有感觉。

这提醒我scipy.spatial具有成对距离的squareformcompact表示。

https://docs.scipy.org/doc/scipy/reference/spatial.distance.html

也许那里有一些有用的东西。