在一个数据框中找到与另一个数据框最近的索引

Find nearest index in one dataframe to another

我是 python 及其图书馆的新手。搜索了所有论坛,但找不到合适的解决方案。这是第一次在这里发布问题。对不起,如果我做错了什么。

所以,我有两个 DataFrame,如下所示,包含 X Y Z 坐标 (UTM) 和其他特征。

In [2]: a = {
   ...:     'X': [1, 2, 5, 7, 10, 5, 2, 3, 24, 21],
   ...:     'Y': [3, 4, 8, 15, 20, 12, 23, 22, 14, 7],
   ...:     'Z': [12, 4, 9, 16, 13, 1, 8, 17, 11, 19],
   ...: }
   ...:
In [3]: b = {
   ...:     'X': [1, 8, 20, 7, 32],
   ...:     'Y': [6, 4, 17, 45, 32],
   ...:     'Z': [52, 12, 6, 8, 31],
   ...: }

In [4]: df1 = pd.DataFrame(data=a)
In [5]: df2 = pd.DataFrame(data=b)
In [6]: print(df1)
    X   Y   Z
0   1   3  12
1   2   4   4
2   5   8   9
3   7  15  16
4  10  20  13
5   5  12   1
6   2  23   8
7   3  22  17
8  24  14  11
9  21   7  19

In [7]: print(df2)
    X   Y   Z
0   1   6  52
1   8   4  12
2  20  17   6
3   7  45   8
4  32  32  31

我需要找到 df1 中与 df2 中每个点的最近点(距离)并创建新的 DataFrame。

所以我写了下面的代码,实际上找到了离df2.iloc[0]最近的点(距离)。

In [8]: x = (
   ...:     np.sqrt(
   ...:         ((df1['X'].sub(df2["X"].iloc[0]))**2)
   ...:         .add(((df1['Y'].sub(df2["Y"].iloc[0]))**2))
   ...:         .add(((df1['Z'].sub(df2["Z"].iloc[0]))**2))
   ...:     )
   ...: ).idxmin()

In [9]: x1 = df1.iloc[[x]]
In[10]: print(x1)
   X   Y   Z
3  7  15  16

所以,我想我需要一个循环来遍历 df2 并将上面的代码应用于每一行。因此,我需要一个新的更新的 df1,其中包含与 df2 的每个点最近的所有点。但是做不到。请指教。

这实际上是一个很好的例子,说明 numpy 的广播规则比 pandas 具有明显的优势。

手动对齐df1的坐标为列向量(通过引用df1[[col]].to_numpy())和df2的坐标为行向量(df2[col].to_numpy()),我们可以得到每个数据帧中的每个元素到每个数据帧中的每个元素的距离另一个很快自动广播:

In [26]: dists = np.sqrt(
    ...:     (df1[['X']].to_numpy() - df2['X'].to_numpy()) ** 2
    ...:     + (df1[['Y']].to_numpy() - df2['Y'].to_numpy()) ** 2
    ...:     + (df1[['Z']].to_numpy() - df2['Z'].to_numpy()) ** 2
    ...: )

In [27]: dists
Out[27]:
array([[40.11234224,  7.07106781, 24.35159132, 42.61455151, 46.50806382],
       [48.05205511, 10.        , 22.29349681, 41.49698784, 49.12229636],
       [43.23193264,  5.83095189, 17.74823935, 37.06750599, 42.29657197],
       [37.58989226, 11.74734012, 16.52271164, 31.04834939, 33.74907406],
       [42.40283009, 16.15549442, 12.56980509, 25.67099531, 30.85449724],
       [51.50728104, 13.92838828, 16.58312395, 33.7934905 , 45.04442252],
       [47.18050445, 20.32240143, 19.07878403, 22.56102835, 38.85871846],
       [38.53569774, 19.33907961, 20.85665361, 25.01999201, 33.7194306 ],
       [47.68647607, 18.89444363,  7.07106781, 35.48239   , 28.0713377 ],
       [38.60051813, 15.06651917, 16.43167673, 41.96427052, 29.83286778]])

Argmin 现在将为您提供正确的位置索引向量:

In [28]: dists.argmin(axis=0)
Out[28]: array([3, 2, 8, 6, 8])

或者,select 来自 df1 的适当值:

In [29]: df1.iloc[dists.argmin(axis=0)]
Out[29]:
    X   Y   Z
3   7  15  16
2   5   8   9
8  24  14  11
6   2  23   8
8  24  14  11

编辑

一个答案在我的后面弹出,然后被删除,其中引用了 scipy.spatial.distance_matrix,计算 dists 与:

distance_matrix(df1[list('XYZ')].to_numpy(), df2[list('XYZ')].to_numpy())

不确定为什么删除该答案,但这似乎是一种非常好的、干净的方法来获取我在上面手动生成的数组!

性能说明

请注意,如果您只是想获得最接近的值,则无需求平方根,因为与 dist**2 上的加法、减法、幂和排序相比,这是一项代价高昂的操作仍然有效。

首先,您定义一个函数,returns 使用 numpy.where 最近的点。然后通过 df2.[​​=12=] 使用 apply 函数到 运行

import pandas as pd
import numpy as np
a = {
   'X': [1, 2, 5, 7, 10, 5, 2, 3, 24, 21],
   'Y': [3, 4, 8, 15, 20, 12, 23, 22, 14, 7],
   'Z': [12, 4, 9, 16, 13, 1, 8, 17, 11, 19]
 }
b = {
   'X': [1, 8, 20, 7, 32],
   'Y': [6, 4, 17, 45, 32],
   'Z': [52, 12, 6, 8, 31]
 }
df1 = pd.DataFrame(a)
df2 = pd.DataFrame(b)

dist = lambda dx,dy,dz: np.sqrt(dx**2+dy**2+dz**2)

def closest(row):
    darr = dist(df1['X']-row['X'], df1['Y']-row['Y'], df1['Z']-row['Z'])
    idx = np.where(darr == np.amin(darr))[0][0]
    return df1['X'][idx], df1['Y'][idx], df1['Z'][idx]

df2['closest'] = df2.apply(closest, axis=1)

print(df2)

输出:

    X   Y   Z       closest
0   1   6  52   (7, 15, 16)
1   8   4  12     (5, 8, 9)
2  20  17   6  (24, 14, 11)
3   7  45   8    (2, 23, 8)
4  32  32  31  (24, 14, 11)