python - 如何选择满足条件的最近邻居?
问题描述
问题:如何为地理数据框中的所有元素选择“满足条件的 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'
如图所示,它选择了正确的点。但是,有两个问题:
- 没有明确的方法来选择“足够”的邻居。中间是否有足够的蓝点。我不会抓住橙色点。
- 当谈论密度变化很大的数百万个点时,这种扩展性很差。选择 100 个邻居来找到 4 个会导致处理时间方面的损失。
这似乎是一个有人会在某个时候解决的问题。任何指针?我应该考虑sklearn吗?
解决方案
正如评论中提到的,您可以使用 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 是矢量化的。
推荐阅读
- java - 计数器在千分尺中是线程安全的吗
- ios - React-native-firebase:推送通知并不总是在 iOS 上工作
- database - 如何使用碳在一个月内获得工作日期?
- swift - 销毁 UICollectionView 中的滚动视频单元格以获得类似 TikTok 的功能
- asp.net-mvc - 尝试排序时无法翻译 LINQ 表达式
- android - 有没有可以从 firestore 获取所有文档然后使用其中的数据的方法?
- bash - 如何在 crontab 中匹配正则表达式
- eclipse - 如何修复此“指定了未知版本的 Tomcat”(Windows)
- node.js - TypeError:喜欢时无法读取未定义的属性“地图”
- python - 使用 BeautifulSoup 抓取数据时返回空 DataFrame