簡體   English   中英

x兩條點之間的距離

[英]x distance between two lines of points

我有兩個1D numpy數組A和B,分別是大小(n,)和(m,),它們對應於一行上點的x位置。 我想計算A中每個點到B中每個點之間的距離。然后我需要在設定的y距離d處使用這些距離來計算A中每個點的電位。

我目前正在使用以下內容:

V = numpy.zeros(n)
for i in range(n):
    xdist = A[i] - B
    r = numpy.sqrt(xdist**2 + d**2)
    dV = 1/r
    V[i] = numpy.sum(dV)

這適用於大型數據集可能需要一段時間,所以我想使用類似於scipy.spatial.distance.cdist的函數,它不適用於1D數組,我不想為數組添加另一個維度因為它們變得太大了。

矢量化方法

在使用np.newaxis/None引入新軸並因此利用broadcastingA擴展到2D之后的一種矢量化方法將是 -

(1/(np.sqrt((A[:,None] - B)**2 + d**2))).sum(1)

大陣列的混合方法

現在,對於大型數組,我們可能必須將數據划分為塊。

因此,使用BSZ作為塊大小,我們將采用混合方法,如此 -

dsq = d**2   
V = np.zeros((n//BSZ,BSZ))
for i in range(n//BSZ):
    V[i] = (1/(np.sqrt((A[i*BSZ:(i+1)*BSZ,None] - B)**2 + dsq))).sum(1)

運行時測試

方法 -

def original_app(A,B,d):
    V = np.zeros(n)
    for i in range(n):
        xdist = A[i] - B
        r = np.sqrt(xdist**2 + d**2)
        dV = 1/r
        V[i] = np.sum(dV)
    return V

def vectorized_app1(A,B,d):
    return (1/(np.sqrt((A[:,None] - B)**2 + d**2))).sum(1)

def vectorized_app2(A,B,d, BSZ = 100):
    dsq = d**2   
    V = np.zeros((n//BSZ,BSZ))
    for i in range(n//BSZ):
        V[i] = (1/(np.sqrt((A[i*BSZ:(i+1)*BSZ,None] - B)**2 + dsq))).sum(1)
    return V.ravel()

時間和驗證 -

In [203]: # Setup inputs
     ...: n,m = 10000,2000
     ...: A = np.random.rand(n)
     ...: B = np.random.rand(m)
     ...: d = 10
     ...: 

In [204]: out1 = original_app(A,B,d)
     ...: out2 = vectorized_app1(A,B,d)
     ...: out3 = vectorized_app2(A,B,d, BSZ = 100)
     ...: 
     ...: print np.allclose(out1, out2)
     ...: print np.allclose(out1, out3)
     ...: 
True
True

In [205]: %timeit original_app(A,B,d)
10 loops, best of 3: 133 ms per loop

In [206]: %timeit vectorized_app1(A,B,d)
10 loops, best of 3: 138 ms per loop

In [207]: %timeit vectorized_app2(A,B,d, BSZ = 100)
10 loops, best of 3: 65.2 ms per loop

我們可以使用參數塊大小BSZ -

In [208]: %timeit vectorized_app2(A,B,d, BSZ = 200)
10 loops, best of 3: 74.5 ms per loop

In [209]: %timeit vectorized_app2(A,B,d, BSZ = 50)
10 loops, best of 3: 67.4 ms per loop

因此,最好的一個似乎是在我的結尾提供2x倍的加速,塊大小為100

編輯:仔細觀察后,我的回答幾乎與Divakar的相同。 但是,您可以通過就地執行操作來節省一些內存。 沿第二軸取總和比第一軸長。

import numpy

a = numpy.random.randint(0,10,10) * 1.
b = numpy.random.randint(0,10,10) * 1.

xdist = a[:,None] - b
xdist **= 2
xdist += d**2
xdist **= -1
V = numpy.sum(xdist, axis=1)

它提供與您的代碼相同的解決方案。

我想使用類似於scipy.spatial.distance.cdist的函數,它不適用於1D數組,我不想為數組添加另一個維度,因為它們變得太大了。

cdist工作正常,你只需要重新整形數組以形成(n,1)而不是(n,)。 您可以通過使用A[:, None]A.reshape(-1, 1)將一個維度添加到一維數組A而不復制基礎數據。

例如,

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

In [57]: A
Out[57]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [58]: B
Out[58]: array([0, 2, 4, 6, 8])

In [59]: A[:, None]
Out[59]: 
array([[0],
       [1],
       [2],
       [3],
       [4],
       [5],
       [6],
       [7],
       [8],
       [9]])

In [60]: cdist(A[:, None], B[:, None])
Out[60]: 
array([[ 0.,  2.,  4.,  6.,  8.],
       [ 1.,  1.,  3.,  5.,  7.],
       [ 2.,  0.,  2.,  4.,  6.],
       [ 3.,  1.,  1.,  3.,  5.],
       [ 4.,  2.,  0.,  2.,  4.],
       [ 5.,  3.,  1.,  1.,  3.],
       [ 6.,  4.,  2.,  0.,  2.],
       [ 7.,  5.,  3.,  1.,  1.],
       [ 8.,  6.,  4.,  2.,  0.],
       [ 9.,  7.,  5.,  3.,  1.]])

要計算代碼中顯示的V ,可以使用cdist with metric='sqeuclidean' ,如下所示:

In [72]: d = 3.

In [73]: r = np.sqrt(cdist(A[:,None], B[:,None], metric='sqeuclidean') + d**2)

In [74]: V = (1/r).sum(axis=1)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM