当前位置: 首页 > 面试题库 >

使用numpy在python中进行向量化空间距离

湛宏旷
2023-03-14
问题内容

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

使用scipy很容易:

import scipy
D = spdist.cdist(verts, verts)

但是由于引入新依赖项的项目政策,我无法使用它。

所以我想出了这个天真的代码:

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距离,其余代码仅对1D数组中的顶点进行迭代,并且对于每个顶点,它都计算同一数组中彼此之间的距离并生成2D距离数组。

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

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

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

更新:我能够加快速度,如下所示:

    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的解决方案


问题答案:

有一个eucl_dist
软件包(免责声明:我是它的作者),其中基本上包含两种方法来解决计算平方欧几里德距离的问题,该方法比的效率更高SciPy's cdist,特别是对于大型数组(具有相当大的列数)。

我们将使用其中的一些代码source code来适应此处的问题,从而为我们提供两种方法。

方法1

wiki contents,我们可以利用matrix-multiplication一些NumPy specific implementations我们的第一个方法,像这样-

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源代码链接中再次进行了详细讨论,以获取我们的第二种方法,这种方法适用于较小的列,例如此处所示-

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()是否需要的最终输出。

样品运行-

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点-

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在性能上接近一种!



 类似资料:
  • 问题内容: 我正在尝试使用Haversine公式来计算由纬度和经度标识的一长串位置的距离矩阵,该公式采用两个坐标对的元组来产生距离: 我可以使用嵌套的for循环计算所有点之间的距离,如下所示: 使用一个简单的函数: 但是考虑到时间的复杂性,这需要花费相当长的时间,大约需要20秒才能获得500点,而且我的清单要长得多。这让我着眼于矢量化,并且遇到了((docs)),但无法弄清楚如何在这种情况下应用它

  • 问题内容: 我有2 x 4和3 x 4的矩阵。我想找到各行之间的欧几里得距离,并在最后得到2 x 3的矩阵。这是一个带for循环的代码,它针对所有b行向量计算a中每个行向量的欧式距离。在不使用for循环的情况下该如何做? 问题答案: 只需在正确的位置使用:

  • 问题内容: 假设我有一个形状为(1,256)的行向量。我想将其转换为形状为(256,1)的列向量。您在Numpy中会如何做? 问题答案: 您可以使用 转置 操作来执行此操作: 例: 请注意,原始数组仍将保持不变。转置操作只会复制并转置它。 如果输入阵列是相当1D中,则可以 促进 通过引入新的(singleton)的轴作为所述第二尺寸数组的列向量。下面是一个示例: 对于一维情况,还有另一个选择是使用

  • 问题内容: 我想在python中使用线程下载大量网页,并通过以下代码在网站之一中使用队列。 它放置了一个无限的while循环。是否每个线程都连续运行,直到所有线程完成才结束?我错过了什么吗? 问题答案: 将线程设置为线程会使线程在主线程完成后退出。但是,是的,您是正确的,只要线程中的某些内容将阻塞,线程将连续运行。 该文档解释了此详细信息Queue docs python Threading文档也

  • 问题内容: 当我运行此代码时: 但是我得到警告 所以我去看了看文档,但是“没有文档”。但仍然没有提到初始化向量是什么以及如何使用它。谁能启发我? 我知道我可以做更多的Google搜索工作,但是在众多搜索结果中排在首位,我认为这个问题对其他遇到此问题的人可能很有用。 问题答案: IV通常是一个随机数,可确保加密文本是唯一的。 为了解释为什么需要它,让我们假设我们有一个用密钥“秘密”而不用IV加密的人

  • 我有一个用户的口味表: 我有电影类型内容表: 我正在尝试获取每个用户的偏好向量,并获取其与电影内容的相似性度量,以便通过点积推荐最偏好的电影: 为了计算距离,我首先通过以下方式标准化了用户的偏好表: 我不明白为什么规范化后表示不同的偏好?例如,第三个值和第五个值都是,但在标准化后,我得到了和,或者最大值转换为,这在点积之后给出的相似性值较小。 在计算点积之前对数据进行规范化是否正确?如果是,我做得