从两个 Pandas DataFrames 向数据帧添加一列,当前使用两个带有条件的循环:有更快的方法吗?

Posted

技术标签:

【中文标题】从两个 Pandas DataFrames 向数据帧添加一列,当前使用两个带有条件的循环:有更快的方法吗?【英文标题】:adding a column to a dataframe from two Pandas DataFrames and currently using two loops with a conditional: Is there a faster way? 【发布时间】:2021-11-04 17:11:24 【问题描述】:

我目前正在数据框中循环遍历 GPS 坐标。我正在使用此循环查看具有特定位置的 GPS 坐标的另一个数据帧,并使用最近的位置更新原始数据帧。这工作正常,但它非常慢。有更快的方法吗?

这里是示例数据:

进口:

from shapely.geometry import Point
import pandas as pd
from geopy import distance

创建示例 df1

gps_points = [Point(37.773972,-122.431297) , Point(35.4675602,-97.5164276) , Point(42.35843, -71.05977)]
df_gps = pd.DataFrame()
df_gps['points'] = gps_points

创建示例 df2

locations = 'location':['San Diego', 'Austin', 'Washington DC'],
        'gps':[Point(32.715738 , -117.161084), Point(30.267153 , -97.7430608), Point(38.89511 , -77.03637)]
df_locations = pd.DataFrame(locations)

两个循环和更新:

lst = [] #create empty list to populate new df column
for index , row in df_gps.iterrows(): # iterate over first dataframe rows
    point = row['points'] # pull out GPS point
    closest_distance = 999999 # create container for distance
    closest_location = None #create container for closest location
    for index1 , row1 in df_locations.iterrows(): # iterate over second dataframe
        name = row1['location'] # assign name of location
        point2 = row1['gps'] # assign coordinates of location
        distances = distance.distance((point.x , point.y) , (point2.x , point2.y)).miles # calculate distance
        if distances < closest_distance: # check to see if distance is closer
            closest_distance = distances # if distance is closer assign it
            closest_location = name # if distance is closer assign name
    lst.append(closest_location) # append closest city
df_gps['closest_city'] = lst # add new column with closest cities

我真的很想以最快的方式做到这一点。我已经阅读了 pandas 的矢量化,并考虑过创建一个函数,然后使用 How to iterate over rows in a DataFrame in Pandas 中提到的 apply ,但是我的代码中需要两个循环和一个条件,这样模式就会失效。谢谢你的帮助。

【问题讨论】:

您的数据框真的是 Pandas 数据框还是带有几何列的 Geopandas 数据框? 只是熊猫。就像样本数据一样。真正的问题是优化循环 【参考方案1】:

您可以使用来自 Scipy 的KDTree:

from scipy.spatial import KDTree

# Extract lat/lon from your dataframes
points = df_gps['points'].apply(lambda p: (p.x, p.y)).apply(pd.Series)
cities = df_locations['gps'].apply(lambda p: (p.x, p.y)).apply(pd.Series)

distances, indices = KDTree(cities).query(points)

df_gps['closest_city'] = df_locations.iloc[indices]['location'].values
df_gps['distance'] = distances

您可以使用np.where 过滤掉太远的距离。

对于性能,请检查 my answer 是否存在类似问题,df_gps 为 25k 行,df_locations 为 200k。

【讨论】:

科拉连。谢谢你。完美的回应,感谢您提供我希望在发布之前找到的文章的链接。 很好奇 KDTree 如何处理地球几何?如果我理解正确的方法,它将坐标放入二维平面,然后选择最近的邻居,而不是进行任何真正的地理空间距离计算?这就是返回的距离不是英里或类似值的原因。您是否注意到这种方法的投影错误? 尽管该解决方案在某些情况下有效,但并非在所有情况下都有效。这个页面帮助kanoki.org/2019/12/27/… scikit-learn.org/stable/modules/generated/… 也可以帮助那些人。 你也可以看看这个帖子吗:***.com/a/67780643/15239951。不要犹豫,投票:)【参考方案2】:

基于 Corralien 的洞察,代码中的最终答案:

from sklearn.neighbors import BallTree, DistanceMetric

points = df_gps['points'].apply(lambda p: np.radians((p.x, p.y))).apply(pd.Series)
cities = df_locations['gps'].apply(lambda p: np.radians((p.x, p.y))).apply(pd.Series)
dist = DistanceMetric.get_metric('haversine')
tree = BallTree(cities, metric=dist)
dists, cities = tree.query(points)
df_gps['dist'] = dists.flatten() * 3956
df_gps['closest_city'] = df_locations.iloc[cities.flatten()]['location'].values

【讨论】:

干得好。我更新了您的帖子以包含来自 sklearn 的模块,以便其他用户可以复制。 +1 x 2 没有问题,我可以接受你的。

以上是关于从两个 Pandas DataFrames 向数据帧添加一列,当前使用两个带有条件的循环:有更快的方法吗?的主要内容,如果未能解决你的问题,请参考以下文章

根据两个 pandas DataFrames 之间的条件为新列分配值

使用函数调整 Pandas Dataframes 索引

Pandas 通过两列左连接 DataFrames

基于文本字段在 Pandas Dataframes 中合并 CSV 文件

Pandas:在具有不同名称的字段上加入 DataFrames?

pandas,读取或存储DataFrames的数据到mysql中