Pandas经纬度计算最近零售门店:替代逐行迭代的向量化方案
房屋匹配最近超市的代码性能优化方案
原有代码的核心性能瓶颈
- 逐行调用
iterrows()遍历DataFrame属于pandas低性能操作,逐行用.at()赋值会频繁触发DataFrame元数据更新,额外开销远大于计算本身 - 每次循环都覆写超市数据集的
dis_from_house列,重复修改表结构产生大量无意义消耗 - 纯Python层面的双重循环计算距离,没有利用批量计算的性能优势
方案1:最小改动版(保留原有距离计算逻辑,性能提升3~10倍)
不需要替换你已经在使用的测地线距离计算逻辑,只需要把循环内的DataFrame操作全部移到循环外,用列表收集结果后一次性赋值,避免循环内修改表结构:
from geopy.distance import geodesic # 提前把超市的坐标、品牌转成原生Python列表,减少循环内pandas取值开销 supermarket_locs = list(zip(adjusted_supermarket_df['lat_wgs'], adjusted_supermarket_df['long_wgs'])) supermarket_brands = adjusted_supermarket_df['retailer'].tolist() match_result = [] total_count = len(small_housing_df) # 直接zip遍历经纬度列,比iterrows()快2倍以上 for idx, (house_lat, house_lon) in enumerate(zip(small_housing_df['latitude'], small_housing_df['longitude'])): house_loc = (house_lat, house_lon) # 计算当前房屋到所有超市的距离,和你原有的列表推导逻辑完全一致 all_distance = [geodesic(house_loc, s_loc).km for s_loc in supermarket_locs] # 找到最近超市的索引,把结果存在临时列表里,不操作原DataFrame min_dis_idx = all_distance.index(min(all_distance)) match_result.append(supermarket_brands[min_dis_idx]) print(f"{idx+1} of {total_count}") # 所有计算完成后,一次性给房屋数据集新增列,全程只做1次DataFrame修改 small_housing_df['closest_store_brand'] = match_result
这个改法和你原有代码的计算结果完全一致,没有额外依赖,只调整了赋值逻辑就能获得数倍性能提升。
方案2:高性能空间计算版(万级数据秒级返回,性能提升100倍以上)
如果你的数据集规模较大(房屋/超市点位过万),纯Python循环的性能还是会遇到瓶颈,推荐用空间索引做最近邻检索,把时间复杂度从O(房屋数*超市数)降到O((房屋数+超市数)*log(超市数)):
import numpy as np from sklearn.neighbors import BallTree # 球树计算地理距离需要先把经纬度转成弧度 house_rad = np.deg2rad(small_housing_df[['latitude', 'longitude']].values) market_rad = np.deg2rad(adjusted_supermarket_df[['lat_wgs', 'long_wgs']].values) # 构建地理空间球树索引 ball_tree = BallTree(market_rad, metric='haversine') # 批量查询每个房屋最近的1个超市,返回距离和对应超市的索引 distance, nearest_idx = ball_tree.query(house_rad, k=1) # 批量匹配超市品牌,一次性赋值 small_housing_df['closest_store_brand'] = adjusted_supermarket_df.iloc[nearest_idx.flatten()]['retailer'].values # 如需保留最近距离,可直接新增列(乘以地球半径6371得到公里单位) small_housing_df['nearest_market_dis_km'] = distance.flatten() * 6371
注意:这个方案用哈弗辛公式计算球面距离,和geopy测地线距离的误差通常在0.5%以内,绝大多数业务场景都可以直接使用。如果需要和原有计算精度完全一致,可以替换为geopandas的sjoin_nearest方法,指定测地线距离作为计算指标。
内容的提问来源于stack exchange,提问作者Violatic
相关产品推荐
相关产品推荐

