【问题标题】:Vectorizing Outer Loop of euclidean distance using numpy on multi-dimensional data在多维数据上使用 numpy 对欧几里得距离的外环进行矢量化
【发布时间】:2016-07-17 22:28:45
【问题描述】:

我有一个二维值矩阵。每行都是一个数据点。

data = np.array(
   [[2, 2, 3],
    [4, 2, 4],
    [1, 1, 4]])

现在,如果我的测试点是单个 1D numpy 数组,例如:

test = np.array([2,3,3])

我可以做一些简单的事情,比如np.sqrt(np.sum((test-data)**2,axis=1)) 来计算测试点相对于所有三个数据点的距离。

但是,如果 test 本身是要测试的点的二维数组,则上述方法不起作用,我一直在使用类似的东西:

test = np.array([[2,3,3],[4,1,2]])    
for i in range(len(test)):
    print np.sqrt(np.sum((test[i]-data)**2,axis=1))

>>> [ 1.          2.44948974  2.44948974]
    [ 2.44948974  2.23606798  3.60555128]

为了根据数据集中的所有点计算我的测试集中的每个点。似乎应该有一种方法可以对整个操作进行矢量化处理,这样我就可以在没有外部 FOR 循环的情况下得到一个对应距离的 (2,3) 矩阵

(注意:虽然这个特定示例是关于欧几里德距离的,但我发现自己有类似类型的操作,我想对一个矩阵的所有元素和另一个矩阵的单个元素执行操作,所以我希望有一种使用 Numpy 设置此类问题的通用方法。)

【问题讨论】:

  • 这似乎可行,但我担心较大数据集上的内存使用情况,因为它似乎需要将每个测试点复制 N 次,其中 N 是开始的数据点数。因此,如果有 1000 个数据点,我需要构建一个 2000 点矩阵来测试两个值。 print np.reshape(np.sqrt(np.sum((np.reshape(np.repeat(test, len(data), axis=0), (len(test) * len(data), Xdims)) - ml.repmat(data, 2, 1)) ** 2, axis=1)), (2, len(data))).T
  • 只需使用 scipy's cdist : from scipy.spatial.distance import cdist ; out = cdist(test,data)。超级高效。

标签: python arrays numpy


【解决方案1】:

使用broadcasting 来做到这一点:

from numpy.linalg import norm
norm(data-test[:,None],axis=2)

[ 1.          2.44948974  2.44948974]
[ 2.44948974  2.23606798  3.60555128]

一些解释。用不同的形状比较容易理解,比如四点和两点:

ens1 = np.array(
   [[2, 2, 3],
    [4, 2, 4],
    [1, 1, 4],
    [2, 4, 5]])


ens2 = np.array([[2,3,3],
                 [4,1,2]])  


In [16]: ens1.shape
Out[16]: (4, 3)

In [17]: ens2.shape
Out[17]: (2, 3)   

然后:

In [21]: ens2[:,None].shape 
Out[21]: (2, 1, 3) 

添加一个新维度。现在我们可以进行 2X4= 8 减法:

In [22]: (ens1-ens2[:,None]).shape
Out[22]: (2, 4, 3)       

沿最后一个轴取范数,8 个距离:

In [23]: norm(ens1-ens2[:,None],axis=2)
Out[23]: 
array([[ 1.        ,  2.44948974,  2.44948974,  2.23606798],
       [ 2.44948974,  2.23606798,  3.60555128,  4.69041576]])     

【讨论】:

    【解决方案2】:

    np.meshgrid 呢?

    import numpy as np
    
    data = np.array(
       [[2, 2, 3],
        [4, 2, 4],
        [1, 1, 4]])
    
    
    test = np.array([[2,3,3],
                     [4,1,2]])   
    
    
    d = np.arange(0,3)
    t = np.arange(0,2)
    d, t = np.meshgrid(d, t)
    
    # print test[t]
    # print data[d]
    print np.sqrt(np.sum((test[t]-data[d])**2,axis=2))  
    

    输出:

    [[ 1.          2.44948974  2.44948974]
     [ 2.44948974  2.23606798  3.60555128]]
    

    【讨论】:

    • 看到Divakar的帖子后,我会选择scipycdist
    【解决方案3】:

    您可以使用列表推导:

    result = np.array([np.sqrt(np.sum((t - data)**2, axis=1)) for t in test])
    

    【讨论】:

    • 我的理解是理解只是一个花哨的 FOR 循环。我希望利用 numpy 的速度并避免 Python 中的循环。
    猜你喜欢
    • 2018-11-13
    • 1970-01-01
    • 2012-03-13
    • 2016-02-18
    • 2015-09-23
    • 2016-08-06
    • 1970-01-01
    • 2019-11-23
    相关资源
    最近更新 更多