首页 > 解决方案 > 如何选择满足条件的最近邻居?

问题描述

问题:如何为地理数据框中的所有元素选择“满足条件的 n 个最近邻”?

示例: “对于森林中的所有树木,半径 100 m 内两棵最高的松树的高度是多少?” (请注意,“树”不一定是“松树”。)

如果我只想要每棵树最近的相邻树,我可以使用

libpysal.weights.KNN.from_dataframe(df_g, k=2, radius=100)

(给定一个地理数据框)

我正在寻找一种方法来获得满足条件的最近邻居。

工作示例

这段代码定义了一个包含 9 个点的地理数据框:

import pandas as pd, libpysal, geopandas as gp,matplotlib.pyplot as plt
from shapely.wkt import loads

# 18 points with values and types
points=['POINT (0.1 0.2)','POINT (-1 0)','POINT (1 0)','POINT (0 -1)','POINT (0 1)','POINT (-2 0)','POINT (2 0)','POINT (0 -2)','POINT (0 2)']
values=[9,8,7,6,5,4,3,2,1]
types=[0,0,0,0,0,1,1,1,1]

df=pd.DataFrame({'points':points,'value':values,'types':types})
gdf=gp.GeoDataFrame(df,geometry=[loads(x) for x in df.points])

我想在半径为 2 的范围内寻找类型为 1 的邻居。

所以,对于中心点,我想在橙色点而不是蓝色点之间寻找邻居:

带类型的点

如果类型不是问题,我可以遍历最近的邻居,例如:

knn2 = libpysal.weights.KNN.from_dataframe(gdf, k=2,radius=2)
for index,row in gdf.iterrows(): # Looping over all points
    knn_neighbors = knn2.neighbors[index] # Get neighbors
    knnsubset = gdf.iloc[knn_neighbors] # Get subdataframe
    print("Mean: ",knnsubset['value'].mean()) # Calculating mean of 'value'

对于中心点,这将选择两个绿点,如图所示:

无类型选择

但是,我只想考虑橙色点。

简单的“修复”:

我当然可以选择“足够”的邻居,然后过滤它们:

knn2 = libpysal.weights.KNN.from_dataframe(gdf, k=8,radius=2) # Select enough neighbors
for index,row in gdf.iterrows(): # Looping over all points
    knn_neighbors = knn2.neighbors[index] # Get neighbors
    knnsubset = gdf.iloc[knn_neighbors] # Get subdataframe
    knnsubset=knnsubset[knnsubset.types==1].head(2) #Require type 1 and take the two first
    print("Mean: ",knnsubset['value'].mean()) # Calculating mean of 'value'

简单修复

如图所示,它选择了正确的点。但是,有两个问题:

这似乎是一个有人会在某个时候解决的问题。任何指针?我应该考虑sklearn吗?

标签: pythonpandasspatialgeopandas

解决方案


正如评论中提到的,您可以使用 DistanceBand 而不是 KNN 进行第一次过滤(因为在 KNN 中,您实际上并不知道必须选择多少才能拥有至少 2 棵松树)。

从上面的 gdf ​​开始:

W = libpysal.weights.DistanceBand.from_dataframe(gdf, threshold=2)

results = []  # it is faster to save to list than directly to gdf
for index, row in gdf.iterrows():
    neighbors = W.neighbors[index]
    subset = gdf.loc[neighbors]  # get all within threshold
    limited = subset.loc[subset.types == 1]  # limit to type you want - here you can do more filtering
    if len(limited) > 0:  # if something of type 1 is close enough
        limited['dist'] = limited.geometry.distance(row.geometry)  #get distances
        limited.nsmallest(n=2, columns='dist')
        results.append(list(limited.index))  # save indices of selection
    else:
        results.append(None)

gdf['result'] = results  # save to gdf

这将保存满足条件(或无)的点的索引。

不过,我不确定它对于大型数据集的表现如何,但是 pandas 过滤不应该是一个问题,并且将距离限制为 df 是矢量化的。


推荐阅读