【问题标题】:Find nearest point in other dataframe (WITH A LOT OF DATA)在其他数据框中找到最近的点(有很多数据)
【发布时间】:2020-03-12 14:12:55
【问题描述】:

问题很简单,我有两个 DataFrame:

  • 一个拥有 90 000 套公寓及其纬度/经度

  • 一家拥有 3 000 家药房及其纬度/经度

我想为我所有的公寓创建一个新变量:'最近药房的距离'

为此,我尝试了两种花费大量时间的方法

第一种方法:我创建了一个矩阵,其中我的公寓在行中,我的药房在列中,它们之间的距离在交叉路口,之后我只取矩阵的最小值来有一列90 000 个值的向量

我只是在 numpy 中使用双精度:

m,n=len(result['latitude']),len(pharma['lat'])
M = np.ones((m,n))
for i in range(m):
     for j in range(n):
        if (result['Code departement'][i]==pharma['departement'][j]):
            M[i,j] =(pharma['lat'][j]-result['latitude'][i])**2+(pharma['lng'][j]-result['longitude'] [i])**2

ps:我知道纬度/经度的公式是错误的,但公寓位于同一地区,所以这是一个很好的近似值

第二种方法:我使用这个主题的解决方案(谁是同样的问题但数据较少) https://gis.stackexchange.com/questions/222315/geopandas-find-nearest-point-in-other-dataframe

我使用了 geopandas 等最近的方法:

from shapely.ops import nearest_points
pts3 = pharma.geometry.unary_union


def near(point, pts=pts3):
     nearest = pharma.geometry == nearest_points(point, pts)[1]
     return pharma[nearest].geometry.get_values()[0]

appart['Nearest'] = appart.apply(lambda row: near(row.geometry), axis=1)

正如我所说,两种方法都花费了太多时间,在运行 1 小时后,我的电脑/笔记本电脑崩溃并且失败了。

我的最后一个问题:您有优化方法来加快速度吗?有可能的 ?如果它已经优化,我会购买另一台 PC,但要寻找哪些标准,但要寻找能够进行如此快速计算的 PC 的标准是什么?

【问题讨论】:

  • 我认为您应该遵循您向我们提出的问题的第二个答案,即使用空间索引来避免 all:all 计算距离的答案。
  • 你有例子吗?因为我的印象是我已经在 geopandas 的第二个解决方案中使用了空间索引,这对所花费的时间没有任何改变。
  • 那我误解了你的代码,我之前的评论有误。
  • 澄清一下,基于 shapely 的第二个选项不使用空间索引。
  • 不不,肯定是我不明白什么是空间索引。你有一个例子吗?一个链接?

标签: python pandas optimization nearest-neighbor geopandas


【解决方案1】:

我猜Ball Tree 是适合此任务的结构。

您可以使用scikit-learn 实现,请参阅下面的代码以获取适合您的案例的示例:

import numpy as np
import geopandas as gpd
from shapely.geometry import Point
from sklearn.neighbors import BallTree

## Create the two GeoDataFrame to replicate your dataset
appart = gpd.GeoDataFrame({
        'geometry': Point(a, b),
        'x': a,
        'y': b,
    } for a, b in zip(np.random.rand(100000), np.random.rand(100000))
])

pharma = gpd.GeoDataFrame([{
        'geometry': Point(a, b),
        'x': a,
        'y': b,
    } for a, b in zip(np.random.rand(3000), np.random.rand(3000))
])

# Create a BallTree 
tree = BallTree(pharma[['x', 'y']].values, leaf_size=2)

# Query the BallTree on each feature from 'appart' to find the distance
# to the nearest 'pharma' and its id
appart['distance_nearest'], appart['id_nearest'] = tree.query(
    appart[['x', 'y']].values, # The input array for the query
    k=1, # The number of nearest neighbors
)

使用这种方法,您可以很快解决您的问题(上面的示例,在我的计算机上,在 100000 个点的输入数据集上,用不到一秒的时间在 3000 个点中找到最近点的索引)。

默认情况下,BallTreequery 方法返回到最近邻居的距离及其 id。 如果您愿意,您可以通过将return_distance 参数设置为False 来禁用返回此最近邻居的距离。 如果你真的只关心距离,你可以只保存这个值:

appart['distance_nearest'], _ = tree.query(appart[['x', 'y']].values, k=1)

【讨论】:

  • 哦,谢谢伙计,它的效率和速度如此之快 :o 这是一个非常好的消息 :) 我试图按区域进行分割以减少计算量,它也有效,但比 BallTree 最后一个问题: 在纬度和经度上与球树的距离如何以千米为单位?因为在这里我有一段距离,但我不知道它真正代表的聊天(以及是否有意义)。
  • 我认为您应该转换您的 pharma 和您的 appart 地理数据框以使用投影坐标系(例如法国的“epsg:2154”或欧洲的“epsg:3035”) appart.to_crs(epsg=2154, inplace=True)pharma 相同)。然后通过appart['x'] = appart.geometry.xappart['y'] = appart.geometry.ypharma 相同)创建 x 和 y 列。然后您可以按照我的回答中所述使用ballTree,返回的距离将以米为单位。
  • @ArnaudH 不要犹豫,告诉你是否更喜欢我在我的答案中而不是在 cmets 中扩展我的解释!
  • 评论很完美,但如你所愿:) 我只是将指标更改为球树:tree = BallTree(pharma[['lat_r', 'lng_r']].values, leaf_size=2, metric='haversine') 我将我的度数转换为弧度:appart['latitude_r']=pd.DataFrame(np.deg2rad(appart['latitude'].values)) appart['longitude_r']=pd.DataFrame(np.deg2rad(appart['longitude'].values)) pharma['lat_r']=pd.DataFrame(np.deg2rad(pharma['lat'].values)) pharma['lng_r']=pd.DataFrame(np.deg2rad(pharma['lng'].values)) 它仍然不好,但有点我认为很快就会解决的问题:)
  • 哦,它们是评论中字符的限制,确实,我不明白什么是坐标系,如果你有一些关于这个的文档以及为什么它会解决我的问题打开;)
猜你喜欢
  • 2017-03-05
  • 1970-01-01
  • 1970-01-01
  • 2021-09-21
  • 1970-01-01
  • 2022-01-21
  • 2021-07-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多