在pandas数据框中获取彼此之间的最近点

6

i have a dataframe:

  routeId  latitude_value  longitude_value
  r1       28.210216        22.813209
  r2       28.216103        22.496735
  r3       28.161786        22.842318
  r4       28.093110        22.807081
  r5       28.220370        22.503500
  r6       28.220370        22.503500
  r7       28.220370        22.503500

我希望生成一个类似于以下的数据框df2

routeId    nearest
  r1         r3         (for example)
  r2       ...    similarly for all the routes.

我要实现的逻辑是:对于每条路线,我应该找到所有其他路线的欧几里得距离,并在routeId上进行迭代。
有一个计算欧几里得距离的函数。
dist = math.hypot(x2 - x1, y2 - y1)

但我不确定如何构建一个函数,其中我将传递一个数据框或使用.apply()

def  get_nearest_route():
    .....
    return df2

最接近的数字会小于当前数字吗?(例如,您的值为5,但下一个数字是4.9) - MattR
@Bharath 已经完成,谢谢 :) - Shubham R
4个回答

10

我们可以使用scipy.spatial.distance.cdist或多个for循环,然后用routes替换min并找到最接近的i.e。

mat = scipy.spatial.distance.cdist(df[['latitude_value','longitude_value']], 
                              df[['latitude_value','longitude_value']], metric='euclidean')

# If you dont want scipy, you can use plain python like 
# import math
# mat = []
# for i,j in zip(df['latitude_value'],df['longitude_value']):
#     k = []
#     for l,m in zip(df['latitude_value'],df['longitude_value']):
#         k.append(math.hypot(i - l, j - m))
#     mat.append(k)
# mat = np.array(mat)

new_df = pd.DataFrame(mat, index=df['routeId'], columns=df['routeId']) 

new_df的输出结果

routeId        r1        r2        r3        r4        r5        r6        r7
routeId                                                                      
r1       0.000000  0.316529  0.056505  0.117266  0.309875  0.309875  0.309875
r2       0.316529  0.000000  0.349826  0.333829  0.007998  0.007998  0.007998
r3       0.056505  0.349826  0.000000  0.077188  0.343845  0.343845  0.343845
r4       0.117266  0.333829  0.077188  0.000000  0.329176  0.329176  0.329176
r5       0.309875  0.007998  0.343845  0.329176  0.000000  0.000000  0.000000
r6       0.309875  0.007998  0.343845  0.329176  0.000000  0.000000  0.000000
r7       0.309875  0.007998  0.343845  0.329176  0.000000  0.000000  0.000000    

#Replace minimum distance with column name and not the minimum with `False`.
# new_df[new_df != 0].min(),0). This gives a mask matching minimum other than zero.  
closest = np.where(new_df.eq(new_df[new_df != 0].min(),0),new_df.columns,False)

# Remove false from the array and get the column names as list . 
df['close'] = [i[i.astype(bool)].tolist() for i in closest]


 routeId  latitude_value  longitude_value         close
0      r1       28.210216        22.813209          [r3]
1      r2       28.216103        22.496735  [r5, r6, r7]
2      r3       28.161786        22.842318          [r1]
3      r4       28.093110        22.807081          [r3]
4      r5       28.220370        22.503500          [r2]
5      r6       28.220370        22.503500          [r2]
6      r7       28.220370        22.503500          [r2] 

如果您不想忽略零,则

# Store the array values in a variable
arr = new_df.values
# We dont want to find mimimum to be same point, so replace diagonal by nan
arr[np.diag_indices_from(new_df)] = np.nan

# Replace the non nan min with column name and otherwise with false
new_close = np.where(arr == np.nanmin(arr, axis=1)[:,None],new_df.columns,False)

# Get column names ignoring false. 
df['close'] = [i[i.astype(bool)].tolist() for i in new_close]

   routeId  latitude_value  longitude_value         close
0      r1       28.210216        22.813209          [r3]
1      r2       28.216103        22.496735  [r5, r6, r7]
2      r3       28.161786        22.842318          [r1]
3      r4       28.093110        22.807081          [r3]
4      r5       28.220370        22.503500      [r6, r7]
5      r6       28.220370        22.503500      [r5, r7]
6      r7       28.220370        22.503500      [r5, r6]

它忽略了一个事实,即r5-r6-r7在完全相同的位置,并且它们没有列在彼此的“接近”列中。 - jo9k
是的,我认为这是“亲密度”问题的最佳解决方案。 - jo9k
如果您使用整数索引而不是字符串,请小心,因为如果最接近的ID为0,则在idx和false值的矩阵中将其注册为false。 - Kyle

7

我建议使用scipy.spatial.distance中的pdist函数

matrix = scipy.spatial.distance.pdist(df[['latitude_value', 'longitude_value']], metric='euclidean')

将返回形状为(n,)的压缩距离矩阵,其中计算了所有成对距离。

然后,您可以使用squareform获取方形的成对距离矩阵:

matrix = scipy.spatial.distance.squareform(matrix)

对于每一行matrix[i],您可以找到索引上最大的值,例如matrix[i][j],您知道对于第i个点,它最接近的点是第j个点。


1
但是这将返回形状为(n,)的矩阵。 - Bharath M Shetty
哦,你说得对!这个函数返回压缩的距离矩阵。要获得方形的矩阵,你需要使用squareform(matrix)。我会编辑我的答案,谢谢。 - jo9k
1
我们不需要cdist。如果XA和XB相同,squareform(pdist(X))会给出相同结果,并且应该用于清晰性。 - jo9k

2

虽然无法与 scipy.spatial.distance.pdist 相比,但仍能正常工作。

from itertools import product
import pandas as pd
import math
df['New']=list(zip(df['latitude_value'],df['longitude_value']))
DF=pd.DataFrame(list(product(df.routeId, df.routeId)), columns=['l1', 'l2'])
New=df[['routeId','New']].merge(DF,left_on='routeId',right_on='l1',how='left').merge(df[['routeId','New']],left_on='l2',right_on='routeId')
New['Cal']=New.apply(lambda x : math.hypot(x.New_x[0] - x.New_y[0] , x.New_x[1]  - x.New_y[1] ),axis=1)
New=New.loc[New.l1!=New.l2,:]
New.sort_values('Cal').drop_duplicates(['l1'],keep='first')


Out[386]: 
   routeId_x                   New_x  l1  l2 routeId_y                   New_y       Cal
47        r6     (28.22037, 22.5035)  r6  r7        r7     (28.22037, 22.5035)  0.000000
41        r7     (28.22037, 22.5035)  r7  r6        r6     (28.22037, 22.5035)  0.000000
39        r5     (28.22037, 22.5035)  r5  r6        r6     (28.22037, 22.5035)  0.000000
43        r2  (28.216103, 22.496735)  r2  r7        r7     (28.22037, 22.5035)  0.007998
2         r3  (28.161786, 22.842318)  r3  r1        r1  (28.210216, 22.813209)  0.056505
14        r1  (28.210216, 22.813209)  r1  r3        r3  (28.161786, 22.842318)  0.056505
17        r4   (28.09311, 22.807081)  r4  r3        r3  (28.161786, 22.842318)  0.077188

请对索引进行排序。我认为你犯了一个错误。 - Bharath M Shetty
@Bharath 我想我只是不忽略0...我是对的吗...? - BENY
1
不,你不需要这么做,这太大了。我不知道你怎么想得这么快。我得花一个小时来完成它。 - Bharath M Shetty
@Bharath也许需要多练习一下? :-) - BENY

0

你可以使用 itertuples 迭代 DataFrame

data = {index: ((df['latitude_value'] - lat)**2 + (df['longitude_value'] - long)**2).drop(index).argmin() for index, lat, long in df.itertuples()}
pd.Series(data)

对于大型数据集,这可能需要很长时间


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接