计算pandas数据帧中最近邻居的平均距离

Use*_*rR6 3 python euclidean-distance dataframe pandas

随着时间的推移,我有一组对象及其位置。我想获得每辆车与其最近邻居之间的距离,并计算每个时间点的平均值。示例数据框如下:

 time = [0, 0, 0, 1, 1, 2, 2]
 x = [216, 218, 217, 280, 290, 130, 132]
 y = [13, 12, 12, 110, 109, 3, 56]
 car = [1, 2, 3, 1, 3, 4, 5]
 df = pd.DataFrame({'time': time, 'x': x, 'y': y, 'car': car})
 df

         x       y      car
 time
  0     216     13       1
  0     218     12       2
  0     217     12       3
  1     280     110      1
  1     290     109      3
  2     130     3        4
  2     132     56       5
Run Code Online (Sandbox Code Playgroud)

对于每个时间点,我想知道每辆车最近的汽车邻居。例子:

df2

          car    nearest_neighbour    euclidean_distance  
 time
  0       1            3                    1.41
  0       2            3                    1.00
  0       3            1                    1.41
  1       1            3                    10.05
  1       3            1                    10.05
  2       4            5                    53.04
  2       5            4                    53.04
Run Code Online (Sandbox Code Playgroud)

我知道我可以从How to apply euclidean distance function to a groupby object in pandas dataframe 计算汽车之间的成对距离但是我如何获得每辆车的最近邻居?

之后,使用 groupby 获得每帧距离的平均值似乎很简单,但它的第二步真正让我失望。帮助表示赞赏!

Bac*_*con 5

这可能有点矫枉过正,但您可以使用scikit 中的最近邻居

一个例子:

import numpy as np 
from sklearn.neighbors import NearestNeighbors
import pandas as pd

def nn(x):
    nbrs = NearestNeighbors(n_neighbors=2, algorithm='auto', metric='euclidean').fit(x)
    distances, indices = nbrs.kneighbors(x)
    return distances, indices

time = [0, 0, 0, 1, 1, 2, 2]
x = [216, 218, 217, 280, 290, 130, 132]
y = [13, 12, 12, 110, 109, 3, 56] 
car = [1, 2, 3, 1, 3, 4, 5]
df = pd.DataFrame({'time': time, 'x': x, 'y': y, 'car': car})

#This has the index of the nearest neighbor in the group, as well as the distance
nns = df.drop('car', 1).groupby('time').apply(lambda x: nn(x.as_matrix()))

groups = df.groupby('time')
nn_rows = []
for i, nn_set in enumerate(nns):
    group = groups.get_group(i)
    for j, tup in enumerate(zip(nn_set[0], nn_set[1])):
        nn_rows.append({'time': i,
                        'car': group.iloc[j]['car'],
                        'nearest_neighbour': group.iloc[tup[1][1]]['car'],
                        'euclidean_distance': tup[0][1]})

nn_df = pd.DataFrame(nn_rows).set_index('time')
Run Code Online (Sandbox Code Playgroud)

结果:

      car  euclidean_distance  nearest_neighbour
time                                            
0       1            1.414214                  3
0       2            1.000000                  3
0       3            1.000000                  2
1       1           10.049876                  3
1       3           10.049876                  1
2       4           53.037722                  5
2       5           53.037722                  4
Run Code Online (Sandbox Code Playgroud)

(请注意,在时间 0,汽车 3 的最近邻居是汽车 2。sqrt((217-216)**2 + 1)大约是1.4142135623730951while sqrt((218-217)**2 + 0) = 1


Hal*_*Ali 5

使用cdistfromscipy.spatial.distance得到一个矩阵,表示每辆车到其他每辆车的距离。由于每辆车到自身的距离都是0,所以对角线元素都为0。

示例(对于time == 0):

X = df[df.time==0][['x','y']]
dist = cdist(X, X)
dist
array([[0.        , 2.23606798, 1.41421356],
       [2.23606798, 0.        , 1.        ],
       [1.41421356, 1.        , 0.        ]])
Run Code Online (Sandbox Code Playgroud)

使用np.argsort获取对距离矩阵进行排序的索引。第一列只是行号,因为对角线元素为 0。

idx = np.argsort(dist)
idx
array([[0, 2, 1],
       [1, 2, 0],
       [2, 1, 0]], dtype=int64)
Run Code Online (Sandbox Code Playgroud)

然后,只需使用以下命令选择汽车和最近距离idx

dist[v[:,0], v[:,1]]
array([1.41421356, 1.        , 1.        ])

df[df.time==0].car.values[v[:,1]]
array([3, 3, 2], dtype=int64)
Run Code Online (Sandbox Code Playgroud)

将上述逻辑组合成一个返回所需数据帧的函数:

 def closest(df):
     X = df[['x', 'y']]
     dist = cdist(X, X)
     v = np.argsort(dist)
     return df.assign(euclidean_distance=dist[v[:, 0], v[:, 1]],
                      nearest_neighbour=df.car.values[v[:, 1]])
Run Code Online (Sandbox Code Playgroud)

& 将其与 groupby 一起使用,最后删除索引,因为 groupby-apply 添加了额外的索引

df.groupby('time').apply(closest).reset_index(drop=True)

   time    x    y  car  euclidean_distance  nearest_neighbour
0     0  216   13    1            1.414214                  3
1     0  218   12    2            1.000000                  3
2     0  217   12    3            1.000000                  2
3     1  280  110    1           10.049876                  3
4     1  290  109    3           10.049876                  1
5     2  130    3    4           53.037722                  5
6     2  132   56    5           53.037722                  4
Run Code Online (Sandbox Code Playgroud)

顺便说一句,你的样本输出在时间 0 时是错误的。我的答案和培根的答案都显示了正确的结果