How to (smartly) loop over all points in a GeoDataframe and look at nearest neighbours
我有一个大 (O(10^6) 行) 数据集(带有值的点),我需要对所有点执行以下操作:
- 在预定义的半径内找到最近的 3 个点。
- 计算这三个点的关联值的平均值。
- 将平均值保存到我正在查看的点
“非矢量化”方法是简单地遍历所有点…对于所有点,然后应用逻辑。然而,这扩展性很差。
我已经包含了一个玩具示例,它可以满足我的需求。我已经考虑过的想法是:
- 使用 shapely.ops.nearest_points: 然而,这似乎只返回一个最近的点。
- 围绕每个单独的点进行缓冲并与原始 GeoDataframe 进行连接:这似乎比天真的方法更糟糕。
这是我要实现的逻辑的玩具示例:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import pandas as pd
import numpy as np from shapely.wkt import loads import geopandas as gp points=[ df=pd.DataFrame({‘points’:points,‘values’:values}) for index,row in gdf.iterrows(): # Looping over all points print(gdf) |
生成的 GeoDataframe 在这里:
1
2 3 4 5 6 7 8 9 10 |
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,即3,666667。
如何以更具可扩展性的方式做到这一点?
- 这些是相当多的计算和步骤,您还应该以数据框的形式添加预期的输出。
- 添加了带有评论的输出。
我会为此使用空间索引。您可以使用 libpysal 的功能,它在底层使用 KDTree。对于 2000 个随机点,以下代码运行时间为 3.5 秒,而您的代码运行时间很长(第一分钟后我失去了耐心)。将值保存到列表,然后将列表转换为 DF 的列也可以节省一些时间。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import pandas as pd
import numpy as np from shapely.wkt import loads import geopandas as gp import libpysal points=[ df=pd.DataFrame({‘points’:points,‘values’:values}) knn3 = libpysal.weights.KNN.from_dataframe(gdf, k=3) means = [] subset = gdf.iloc[list(neighbors)] |
这是结果:
1
2 3 4 5 6 7 8 9 10 |
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 |
- 谢谢@martinfleis。这似乎正是我所需要的。我暂时采用了一些类似于 for index,row in gdf.iterrows() 的方法:# 循环遍历所有点 df_tmp=gdf[gdf.geometry.within(row[\\’geometry\\’].buffer( 3))] df_tmp[\\’dist\\’]=df_tmp.geometry.distance(row[\\’geometry\\’]) 但这看起来肯定有好处。谢谢。
- 您可以使用您的方法,但在这种情况下,我建议您对更多点使用空间索引,否则您仍然会相互检查每个点。无论如何,我仍然认为它会更慢。
- 好的,现在我试了一下,它的速度确实提高了几个数量级。好的。不过,我收到一条警告消息:”用户警告:权重矩阵未完全连接。有 159 个组件”。这意味着什么?
- libpysal.weights.KNN 正在生成权重矩阵,即点之间的某种连接。你可以把它想象成一个网络。如果你不能从每个点沿着这个网络到达每个点,这意味着它没有完全连接。出于您的目的,它没有任何后果,您可以愉快地忽略它。
- 行。我担心重合点可能会产生这个错误。再次感谢。
来源:https://www.codenong.com/56709561/