python中数百万行的高效欧几里得距离计算

时间:2019-06-26 10:24:23

标签: python performance distance euclidean-distance

我试图找到两个数据集元素之间的欧几里得距离。每个都有上百万个元素。在计算出欧几里得距离之后,我需要最接近的匹配。鉴于元素数量,它需要几天才能完成

下面是我正在尝试的代码。我也尝试使用与scipy.spatial的距离。但这甚至需要永远

from sklearn.metrics.pairwise import euclidean_distances
df =pd.DataFrame(euclidean_distances(df1,df2))
df.index =  df1.index
df.columns = df2.index
df['min_distance'] = df.min(axis=1)
df['min_distance_id'] = df.idxmin(axis=1)

还有其他方法可以在更短的时间内获得输出。

2 个答案:

答案 0 :(得分:3)

您看过import tensorflow as tf from tensorflow.keras.layers import Input, Dense from tensorflow.keras.models import Model # defining 10 inputs in a List with (X,) shape inputs = [Input(shape = (X,),name='input_{}'.format(k)) for k in range(10)] # defining a shared model with the same weights for all inputs nets = [special_model(inp) for inp in inputs] model = Model(inputs = inputs, outputs = nets) model.compile(optimizer='adam', loss='categorical_crossentropy') 吗?

您可以为其中一个数据集构建此数据结构,并对其进行查询以获取第二个数据集中每个点的距离。

scipy.spatial.cKDTree

我在这里KDTree = scipy.spatial.cKDTree(df1) distances, indexes = KDTree.query(df2, n_jobs=-1) 设置为使用所有可用的处理器。

答案 1 :(得分:0)

我使用numpy为2D点列表编写了此解决方案。它将快速找到两个点阵列之间最接近的点对。我尝试了两张每张一千万点的清单,并在大约4分钟内得到了答案。两侧各有200万点,只用了42秒。我不知道这是否足以满足您的需求,但绝对比“几天”快。如果需要,它还可以为较大的尺寸提供良好的性能。

def closest(A,B):

    def bruteForce(A,B):
        d = None
        swap = A.shape[0] > B.shape[0]
        if swap: A,B = B,A
        for pA in A:
            daB  = np.sum((pA-B)**2,axis=1)
            iMin = np.argmin(daB)
            if d is None or daB[iMin] < d:
                a,b = pA,B[iMin]
                d   = sum((a-b)**2)
        if swap: a,b = b,a
        return a,b,sqrt(d)

    # small sizes are faster using brute force
    if A.shape[0] * B.shape[0] < 1000000 \
    or A.shape[0] < 20 or B.shape[0] < 20:
        return bruteForce(A,B)

    # find center position
    midA  = np.sum(A,axis=0)/A.shape[0]
    midB  = np.sum(B,axis=0)/B.shape[0]
    midAB = (midA+midB)/2

    # closest A to center position
    A2midAB  = np.sum((A-midAB)**2,axis=1)
    iA       = np.argmin(A2midAB)    
    pA       = A[iA]

    # closest B to pA
    B2pA     = np.sum((B-pA)**2,axis=1)
    iB       = np.argmin(B2pA)
    pB       = B[iB]
    dAB      = sqrt(sum((pA-pB)**2))

    # distance of zero is best solution, return immediately
    if dAB == 0: return pA,pB,dAB

    # slope of ptA-ptB segment
    if pA[0] == pB[0]: p,m = 0,1 
    else:              p,m = 1,(pB[1]-pA[1])/(pB[0]-pA[0])

    # perpendicular line intersections with x axis from each point
    xA = m*A[:,1] + p*A[:,0] 
    xB = m*B[:,1] + p*B[:,0]

    # baselines for ptA and ptB
    baseA = xA[iA]
    baseB = xB[iB]
    rightSide = (baseB > baseA) 

    # partitions
    ArightOfA = (xA > baseA) == rightSide
    BrightOfA = (xB > baseA) == rightSide
    AleftOfB  = (xA > baseB) != rightSide
    BleftOfB  = (xB > baseB) != rightSide

    # include pB and exclude pA (we already know its closest point in B)
    ArightOfA[iA] = False
    AleftOfB[iA]  = False
    BleftOfB[iB]  = True
    BrightOfA[iB] = True

    # recurse left side
    if np.any(AleftOfB) and np.any(BleftOfB):
        lA,lB,lD = closest(A[AleftOfB],B[BleftOfB])
        if lD < dAB: pA,pB,dAB = lA,lB,lD

    # resurse right side
    if np.any(ArightOfA) and np.any(BrightOfA):
        rA,rB,rD = closest(A[ArightOfA],B[BrightOfA])
        if rD < dAB: pA,pB,dAB = rA,rB,rD

    return pA,pB,dAB

使用两组随机的2D点进行测试,每个点均具有1000万个点:

dimCount = 2
ACount   = 10000000
ASpread  = ACount
BCount   = ACount-1
BSpread  = BCount
A = np.random.random((ACount,dimCount))*ASpread-ASpread/2
B = np.random.random((BCount,dimCount))*BSpread-BSpread/2

a,b,d = closest(A,B)
print("closest points:",a,b,"distance:",d)

# closest points: [-4422004.2963273   2783038.35968559] [-4422004.76974851  2783038.61468366] distance: 0.5377282447465505

其工作方式是根据策略选择的对(pA,pB)划分A和B点。 pA和pB之间的线用作两个列表中各个点的分区。然后,递归地使用该分区的每一面来查找其他(更接近的)点对。

在图形上,这对应于基于pA-pB段垂直线的分区:

enter image description here

选择pA和pB的策略是找到两组点的近似中心,然后从列表A中选择一个接近该中心的点(pA)。然后在列表B中选择最接近pA的点。这样可以确保在两条垂直线之间没有另一个点在另一个列表中更靠近pA或pB的点。

垂直线相对侧上的A和B点必须比pA-pB彼此远离,因此可以将它们隔离在两个子列表中并分别进行处理。

这允许“分而治之”的方法,大大减少了要比较的点对点距离的数量。

在我的测试中(具有随机分布的点),性能似乎与A和B中的点总数成线性关系。我试图通过创建远不成比例的小点簇来歪曲分布(因此,没有点是实际上在近似中心附近),效果仍然是线性的。我不确定是否有任何“最坏情况”的点分布会导致性能下降(我还没有找到)