如何(智能)遍历GeoDataframe中的所有点并查看最近的邻居

时间:2019-06-21 19:43:49

标签: python pandas geopandas shapely

我有一个很大的(O(10 ^ 6)行)数据集(带有值的点),在这里我需要对所有点进行以下操作:

  • 找到预定义半径内的3个最近点。
  • 计算这三个点的关联值的平均值。
  • 将该平均值保存到我要查看的位置

“非矢量化”方法是简单地循环所有点...针对所有点,然后应用逻辑。但是,伸缩性很差。

我提供了一个玩具示例,可以满足我的要求。我已经考虑过的想法有:

  • 使用shapely.ops.nearest_points:但是,这似乎只返回最近的一个点。
  • 在每个点周围缓冲并与原始GeoDataframe进行连接:看来它的伸缩性甚至比朴素的方法还要差。

这是我要实现的逻辑的一个玩具示例:

import pandas as pd
import numpy as np
from shapely.wkt import loads
import geopandas as gp

points=[
    'POINT (1 1.1)', 'POINT (1 1.9)', 'POINT (1 3.1)',
    'POINT (2 1)', 'POINT (2 2.1)', 'POINT (2 2.9)',
    'POINT (3 0.8)', 'POINT (3 2)', 'POINT (3 3)'
]
values=[9,8,7,6,5,4,3,2,1]

df=pd.DataFrame({'points':points,'values':values})
gdf=gp.GeoDataFrame(df,geometry=[loads(x) for x in df.points], crs={'init': 'epsg:' + str(25832)})

for index,row in gdf.iterrows(): # Looping over all points
    gdf['dist'] = np.nan
    for index2,row2 in gdf.iterrows(): # Looping over all the other points
        if index==index2: continue
        d=row['geometry'].distance(row2['geometry']) # Calculate distance
        if d<3: gdf.at[index2,'dist']=d # If within cutoff: Store
        else: gdf.at[index2,'dist']=np.nan # Otherwise, be paranoid and leave NAN
    # Calculating mean of values for the 3 nearest points and storing 
    gdf.at[index,'mean']=np.mean(gdf.sort_values('dist').head(3)['values'].tolist())

print(gdf)

生成的GeoDataframe在这里:

          points  values       geometry      dist      mean
0  POINT (1 1.1)       9  POINT (1 1.1)  2.758623  6.333333
1  POINT (1 1.9)       8  POINT (1 1.9)  2.282542  7.000000
2  POINT (1 3.1)       7  POINT (1 3.1)  2.002498  5.666667
3    POINT (2 1)       6    POINT (2 1)  2.236068  5.666667
4  POINT (2 2.1)       5  POINT (2 2.1)  1.345362  4.666667
5  POINT (2 2.9)       4  POINT (2 2.9)  1.004988  4.333333
6  POINT (3 0.8)       3  POINT (3 0.8)  2.200000  4.333333
7    POINT (3 2)       2    POINT (3 2)  1.000000  3.000000
8    POINT (3 3)       1    POINT (3 3)       NaN  3.666667

您可以看到上一次迭代的状态。

  • 所有距离都经过计算,最后的距离留在了NAN。
  • 最后一次迭代的平均值是三个最近的点的平均值:2、4和5,即3666667。

我该如何以更可扩展的方式做到这一点?

2 个答案:

答案 0 :(得分:1)

我会为此使用空间索引。您可以使用libpysal的功能,该功能在后台使用KDTree。对于2000个随机点,与您的代码相比,以下代码运行了3.5 s,而且运行了很长时间(我在第一分钟后就失去了耐心)。将值保存到列表中,然后将列表转换为DF列也可以节省一些时间。

import pandas as pd
import numpy as np
from shapely.wkt import loads
import geopandas as gp
import libpysal

points=[
    'POINT (1 1.1)', 'POINT (1 1.9)', 'POINT (1 3.1)',
    'POINT (2 1)', 'POINT (2 2.1)', 'POINT (2 2.9)',
    'POINT (3 0.8)', 'POINT (3 2)', 'POINT (3 3)'
]
values=[9,8,7,6,5,4,3,2,1]

df=pd.DataFrame({'points':points,'values':values})
gdf=gp.GeoDataFrame(df,geometry=[loads(x) for x in df.points], crs={'init': 'epsg:' + str(25832)})

knn3 = libpysal.weights.KNN.from_dataframe(gdf, k=3)

means = []
for index, row in gdf.iterrows(): # Looping over all points
    knn_neighbors = knn3.neighbors[index]
    knnsubset = gdf.iloc[knn_neighbors]
    neighbors = []
    for ix, r in knnsubset.iterrows():
        if r.geometry.distance(row.geometry) < 3: # max distance here
            neighbors.append(ix)

    subset = gdf.iloc[list(neighbors)]
    means.append(np.mean(subset['values']))
gdf['mean'] = means

这是结果:

          points  values       geometry      mean
0  POINT (1 1.1)       9  POINT (1 1.1)  6.333333
1  POINT (1 1.9)       8  POINT (1 1.9)  7.000000
2  POINT (1 3.1)       7  POINT (1 3.1)  5.666667
3    POINT (2 1)       6    POINT (2 1)  5.666667
4  POINT (2 2.1)       5  POINT (2 2.1)  4.666667
5  POINT (2 2.9)       4  POINT (2 2.9)  4.333333
6  POINT (3 0.8)       3  POINT (3 0.8)  4.333333
7    POINT (3 2)       2    POINT (3 2)  3.000000
8    POINT (3 3)       1    POINT (3 3)  3.666667

答案 1 :(得分:0)

这使我想起了我上大学时遇到的数学问题。它与Chapter 7 Example 7密切相关。因此,问题是

  

请考虑某个城镇中的一组移动计算客户端,每个客户端   需要连接到几个可能的基站之一。好   假设有n个客户,每个客户的位置   由其在平面中的(x,y)坐标指定。也有k   基站;每个位置由(x,y)指定   坐标。对于每个客户,我们希望将其准确地连接到   基站之一。我们对连接的选择受到限制   可以通过以下方式进行操作:有一个范围参数r,客户可以   只能连接到距离r以内的基站。那里   也是负载参数L,因此最多只能有L个客户端   连接到任何单个基站。您的目标是设计一个   多项式时间算法可解决以下问题。鉴于   一组客户端和一组基站的位置,以及   范围和负载参数,确定是否每个客户端都可以   同时连接到基站,取决于范围和   上一段中的负载条件。

我相信您可以在多项式时间内将此问题转换为网络流问题,然后假设您仅在O(n * m + cmax)时间内使用修改的ford-fulkerson算法来解决您要寻找的问题向福特富尔克森添加恒定时间操作。这可能不是一个可扩展的问题,可能会出现在多项式时间问题列表中,但这也许比持续运行O(n ^ 2)运行时间更好。

有关如何将其转换为网络流量问题的信息,我将尝试阅读此人的pseudoish code。 pdf密码为鸟