【问题标题】:Find nearest index in one dataframe to another在一个数据框中找到离另一个最近的索引
【发布时间】:2020-10-10 10:28:34
【问题描述】:

我是 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 的每个点的所有最近点。但是来不及了。请指教。

【问题讨论】:

    标签: python pandas dataframe distance nearest-neighbor


    【解决方案1】:

    这实际上是 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])
    

    或者,从 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 上的排序仍然有效。

    【讨论】:

    • 非常感谢您的帮助。两种方法都解决了我的问题。
    【解决方案2】:

    首先,您使用 numpy.where 定义一个返回最近点的函数。然后你使用apply函数运行df2。

    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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-03
      • 1970-01-01
      • 2023-01-03
      • 1970-01-01
      • 1970-01-01
      • 2018-05-14
      • 1970-01-01
      相关资源
      最近更新 更多