使用haversine地理定位双循环生成成员到地点距离排名DataFrame
问题核心原因
你原有代码的问题在于两层循环缩进错误,且没有对每个成员的计算结果做隔离,所有成员的距离数据混在一起排序,自然无法得到每个成员单独的排序结果。以下是可直接运行的修正方案:
方案1:基于你原有逻辑修改的循环版本
import pandas as pd import numpy as np # 这里默认你已经实现了haversine距离计算函数,以下是标准实现可直接用 def haversine(lon1, lat1, lon2, lat2): # 距离单位为公里,可根据需求调整返回值系数 lon1, lat1, lon2, lat2 = map(np.radians, [lon1, lat1, lon2, lat2]) dlon = lon2 - lon1 dlat = lat2 - lat1 a = np.sin(dlat/2)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2)**2 c = 2 * np.arcsin(np.sqrt(a)) return c * 6371 # 存储所有成员的单行结果 all_member_res = [] for m_idx in range(len(members)): # 取当前成员信息 cur_member = members.iloc[m_idx] member_id = cur_member['member_id'] # 计算当前成员到所有地点的距离 dist_list = [] for s_idx in range(len(locations)): cur_loc = locations.iloc[s_idx] dist = haversine( lon1 = cur_member['longitude'], lat1 = cur_member['latitude'], lon2 = cur_loc['longitude'], lat2 = cur_loc['latitude'] ) dist_list.append({ 'location': cur_loc['location'], 'distance': dist }) # 按距离升序排序 cur_member_dist = pd.DataFrame(dist_list).sort_values('distance', ascending=True).reset_index(drop=True) # 拼接为预期的宽表行结构 res_row = { 'member': member_id, 'location_1': cur_member_dist.loc[0, 'location'], 'distance_1': round(cur_member_dist.loc[0, 'distance']), 'location_2': cur_member_dist.loc[1, 'location'], 'distance_2': round(cur_member_dist.loc[1, 'distance']), 'location_3': cur_member_dist.loc[2, 'location'], 'distance_3': round(cur_member_dist.loc[2, 'distance']) } all_member_res.append(res_row) # 生成最终结果表 results = pd.DataFrame(all_member_res)
方案2:更高效的向量化写法,无需嵌套循环
通过笛卡尔积一次性生成所有成员+地点的组合,批量计算距离后直接格式转换,性能远高于嵌套循环,适合数据量更大的场景:
# 生成所有成员和地点的全量组合 cross_df = members.assign(tmp=1).merge(locations.assign(tmp=1), on='tmp', suffixes=('_member', '_loc')).drop('tmp', axis=1) # 批量计算所有组合的距离 cross_df['distance'] = cross_df.apply(lambda x: haversine(x['longitude_member'], x['latitude_member'], x['longitude_loc'], x['latitude_loc']), axis=1) # 按成员分组后给距离做升序排名 cross_df['rank'] = cross_df.groupby('member_id')['distance'].rank(method='first', ascending=True).astype(int) # 转换为预期的宽表结构 results = cross_df.pivot(index='member_id', columns='rank', values=['location', 'distance']).reset_index() # 调整列名和格式,和示例输出对齐 results.columns = [f'{col[0]}_{col[1]}' if col[1] else col[0] for col in results.columns] results = results.rename(columns={'member_id': 'member'}).sort_values('member').reset_index(drop=True) results[['distance_1', 'distance_2', 'distance_3']] = results[['distance_1', 'distance_2', 'distance_3']].round().astype(int)
两种方案运行后得到的results表和你给出的预期输出结构完全一致。
内容的提问来源于stack exchange,提问作者June Smith
相关产品推荐
相关产品推荐

