首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >使用numpy在python中矢量化空间距离

使用numpy在python中矢量化空间距离
EN

Stack Overflow用户
提问于 2018-08-27 05:41:46
回答 1查看 2K关注 0票数 2

我在python中有一个numpy数组,它包含许多(10k+) 3D顶点(坐标为x,y,z的向量)。我需要计算所有可能的这些点对之间的距离。

使用scipy很容易做到:

代码语言:javascript
复制
import scipy
D = spdist.cdist(verts, verts)

但我不能使用它,因为引入新依赖项的项目策略。

所以我想出了这个简单的代码:

代码语言:javascript
复制
def vert_dist(self, A, B):
    return ((B[0]-A[0])**2+(B[1]-A[1])**2+(B[2]-A[2])**2)**(1.0/2)

# Pairwise distance between verts
#Use SciPy, otherwise use fallback
try:
    import scipy.spatial.distance as spdist
    D = spdist.cdist(verts, verts)
except ImportError:
    #FIXME: This is VERY SLOW:
    D = np.empty((len(verts), len(verts)), dtype=np.float64)
    for i,v in enumerate(verts):
        #self.app.setStatus(_("Calculating distance %d of %d (SciPy not installed => using SLOW AF fallback method)"%(i,len(verts))), True)
        for j in range(i,len(verts)):
            D[j][i] = D[i][j] = self.vert_dist(v,verts[j])

vert_dist()计算两个顶点之间的3D距离,其余代码只是迭代一维数组中的顶点,对于每个顶点,它计算同一数组中每个顶点之间的距离,并生成距离的2D数组。

但与scipy的原生C代码相比,这是非常慢的(1000倍)。我想知道我是否可以使用纯numpy来加快速度。至少在某种程度上是这样。

更多信息:https://github.com/scipy/scipy/issues/9172

顺便说一句,我尝试过python编译器,它甚至比纯PyPy还要慢(10倍)。

更新:我可以像这样加快速度:

代码语言:javascript
复制
    def vert_dist_matrix(self, verts):
            #FIXME: This is VERY SLOW:
            D = np.empty((len(verts), len(verts)), dtype=np.float64)
            for i,v in enumerate(verts):
                    D[i] = D[:,i] = np.sqrt(np.sum(np.square(verts-verts[i]), axis=1))
            return D

这通过一次计算整个行消除了内部循环,这使得事情变得非常快,但仍然比scipy慢得多。所以我仍然关注@Divakar的解决方案

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-08-27 05:57:58

有一个eucl_dist包(免责声明:我是它的作者),它基本上包含了两种方法来解决计算欧几里得平方距离的问题,这两种方法比SciPy's cdist更有效,特别是对于大型数组(具有像样的到大量的列)。

我们将使用它的source code中的一些代码来适应这里的问题,为我们提供两种方法。

方法#1

wiki contents之后,我们可以利用matrix-multiplication和一些NumPy specific implementations作为我们的第一种方法,如下所示-

代码语言:javascript
复制
def pdist_squareformed_numpy(a):
    a_sumrows = np.einsum('ij,ij->i',a,a)
    dist = a_sumrows[:,None] + a_sumrows -2*np.dot(a,a.T)
    np.fill_diagonal(dist,0)
    return dist

方法#2

另一种方法是创建输入数组的“扩展”版本,在github源代码链接中也有详细讨论,以使用我们的第二种方法,这对于较少的列更好,就像这里的情况一样:

代码语言:javascript
复制
def ext_arrs(A,B, precision="float64"):
    nA,dim = A.shape
    A_ext = np.ones((nA,dim*3),dtype=precision)
    A_ext[:,dim:2*dim] = A
    A_ext[:,2*dim:] = A**2

    nB = B.shape[0]
    B_ext = np.ones((dim*3,nB),dtype=precision)
    B_ext[:dim] = (B**2).T
    B_ext[dim:2*dim] = -2.0*B.T
    return A_ext, B_ext

def pdist_squareformed_numpy_v2(a):
    A_ext, B_ext = ext_arrs(a,a)
    dist = A_ext.dot(B_ext)
    np.fill_diagonal(dist,0)
    return dist

请注意,这些给出了欧几里德距离的平方。因此,对于实际距离,如果这是最终需要的输出,我们希望使用np.sqrt()

示例运行-

代码语言:javascript
复制
In [380]: np.random.seed(0)
     ...: a = np.random.rand(5,3)

In [381]: from scipy.spatial.distance import cdist

In [382]: cdist(a,a)
Out[382]: 
array([[0.  , 0.29, 0.42, 0.2 , 0.57],
       [0.29, 0.  , 0.58, 0.42, 0.76],
       [0.42, 0.58, 0.  , 0.45, 0.9 ],
       [0.2 , 0.42, 0.45, 0.  , 0.51],
       [0.57, 0.76, 0.9 , 0.51, 0.  ]])

In [383]: np.sqrt(pdist_squareformed_numpy(a))
Out[383]: 
array([[0.  , 0.29, 0.42, 0.2 , 0.57],
       [0.29, 0.  , 0.58, 0.42, 0.76],
       [0.42, 0.58, 0.  , 0.45, 0.9 ],
       [0.2 , 0.42, 0.45, 0.  , 0.51],
       [0.57, 0.76, 0.9 , 0.51, 0.  ]])

In [384]: np.sqrt(pdist_squareformed_numpy_v2(a))
Out[384]: 
array([[0.  , 0.29, 0.42, 0.2 , 0.57],
       [0.29, 0.  , 0.58, 0.42, 0.76],
       [0.42, 0.58, 0.  , 0.45, 0.9 ],
       [0.2 , 0.42, 0.45, 0.  , 0.51],
       [0.57, 0.76, 0.9 , 0.51, 0.  ]])

10k点的计时-

代码语言:javascript
复制
In [385]: a = np.random.rand(10000,3)

In [386]: %timeit cdist(a,a)
1 loop, best of 3: 309 ms per loop

# Approach #1
In [388]: %timeit pdist_squareformed_numpy(a) # squared eucl distances
1 loop, best of 3: 668 ms per loop

In [389]: %timeit np.sqrt(pdist_squareformed_numpy(a)) # actual eucl distances
1 loop, best of 3: 812 ms per loop

# Approach #2
In [390]: %timeit pdist_squareformed_numpy_v2(a) # squared eucl distances
1 loop, best of 3: 237 ms per loop

In [391]: %timeit np.sqrt(pdist_squareformed_numpy_v2(a)) # actual eucl distances
1 loop, best of 3: 395 ms per loop

第二种方法在性能上似乎接近cdist one!

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52030458

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档