从两个 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 中合并 CSV 文件