如何为DataFrame中各地点分配限定半径内的无重复最近关联地点
优化地点分配的Pandas代码实现
需求说明
- 现有两个Pandas DataFrame:df1是目标地点列表(含城市+州、经纬度),df2是待分配地点数据集
- 需将df2中的地点分配给df1中距离最近的地点,确保无重复分配
- 设置最大半径阈值,超过阈值的地点直接排除
数据示例
df1(目标地点)
import pandas as pd city_state = [ ['Austin, Texas', 30.264265060424805, -97.74750518798828], ['San Marcos, Texas', 29.882080, -97.939987], ['Denver, Colorado', 39.7392364, -104.984862] ] df1 = pd.DataFrame(city_state, columns=['Location', 'Latitude', 'Longitude'])
df2(待分配地点)
data = [ ['San Antonio, Texas', 29.425171, -98.494614], ['Oklahoma City, Oklahoma', 35.468491, -97.521263], ['Fort Collins, Colorado', 40.588970, -105.082458], ['Salt Lake City, Utah', 40.758480, -111.888138], ['Springfield, Massachusetts', 42.102051, -72.585762], ['Hartford, Connecticut', 41.764582, -72.6908547] ] df2 = pd.DataFrame(data, columns=['Location', 'Latitude', 'Longitude'])
期望输出格式
{ 'Austin, Texas': [...], 'San Marcos, Texas': ['San Antonio, Texas', ...], 'Denver, Colorado': ['Fort Collins, Colorado', ...] }
当前实现代码
from math import radians, sin, cos, sqrt, atan2 def calculate_distance(lat1, lon1, lat2, lon2): # 转换为弧度 lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2]) # 哈弗辛公式 dlat = lat2 - lat1 dlon = lon2 - lon1 a = sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2 c = 2 * atan2(sqrt(a), sqrt(1 - a)) radius = 3958.8 # 地球半径(英里) distance = radius * c return distance max_radius = 100 result = {} closest_locations_x = {} for _, city_row in df1.iterrows(): city_name = city_row['Location'] city_lat = float(city_row['Latitude']) city_lon = float(city_row['Longitude']) closest_locations = {} for _, location_row in df2.iterrows(): if location_row['Latitude'] != 'Not Found' or location_row['Longitude'] != 'Not Found': location_name = location_row['Location'] location_lat = float(location_row['Latitude']) location_lon = float(location_row['Longitude']) distance = calculate_distance(city_lat, city_lon, location_lat, location_lon) if distance <= max_radius: if location_name not in closest_locations_x: closest_locations[location_name] = distance closest_locations_x[location_name] = distance else: if distance < closest_locations_x[location_name]: result = {key: [value for value in values if value != location_name] for key, values in result.items()} closest_locations[location_name] = distance closest_locations_x[location_name] = distance result[city_name] = list(closest_locations.keys())
当前输出结果
{'Austin, Texas': [], 'San Marcos, Texas': ['San Antonio, Texas'], 'Denver, Colorado': ['Fort Collins, Colorado']}
优化方案
优化思路
- 抛弃嵌套循环,用Pandas向量化运算提升效率
- 先生成所有df2与df1地点的距离矩阵,再筛选符合半径要求的记录
- 通过排序+去重实现“最近且无重复分配”的核心逻辑
优化代码
import pandas as pd from math import radians, sin, cos, sqrt, atan2 def haversine_distance(lat1, lon1, lat2, lon2): lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2]) dlat = lat2 - lat1 dlon = lon2 - lon1 a = sin(dlat/2)**2 + cos(lat1)*cos(lat2)*sin(dlon/2)**2 c = 2 * atan2(sqrt(a), sqrt(1-a)) return 3958.8 * c # 英里 max_radius = 100 # 生成所有df2与df1的地点组合 cross_df = df1.assign(key=1).merge(df2.assign(key=1), on='key').drop('key', axis=1) # 计算每对地点的距离 cross_df['distance'] = haversine_distance(cross_df['Latitude_x'], cross_df['Longitude_x'], cross_df['Latitude_y'], cross_df['Longitude_y']) # 筛选半径内的记录,按距离排序后保留每个df2地点的最近df1分配 valid_assignments = cross_df[cross_df['distance'] <= max_radius].sort_values('distance') unique_assignments = valid_assignments.drop_duplicates(subset='Location_y', keep='first') # 整理为目标字典格式 result = unique_assignments.groupby('Location_x')['Location_y'].apply(list).to_dict() # 补充df1中未分配到地点的条目 for loc in df1['Location']: result.setdefault(loc, []) print(result)
优化说明
- 效率提升:向量化运算替代嵌套循环,处理大数据集时性能优势明显
- 逻辑清晰:分步完成距离计算、筛选、去重、分组,代码可读性更强
- 完整性保障:自动补全df1中无分配记录的地点,确保结果覆盖所有目标地点
- 代码精简:利用Pandas内置方法替代冗余的字典操作,代码量大幅减少
优化后输出结果
{'Austin, Texas': [], 'San Marcos, Texas': ['San Antonio, Texas'], 'Denver, Colorado': ['Fort Collins, Colorado']}
内容的提问来源于stack exchange,提问作者IonicEcommerce
相关产品推荐
相关产品推荐

