基于经纬度与约束条件的地理聚类Python实现求助
带约束的空间聚类实现思路与代码
核心思路
你的需求属于带空间距离约束(最大7公里)和人口规模约束(最小5000)的空间聚类,常规聚类算法(如K-means、普通DBSCAN)无法直接满足双约束,推荐使用贪心迭代聚类结合球面距离计算,核心逻辑如下:
- 优先从人口规模大的点位入手,确保快速满足人口阈值
- 用Haversine公式计算点位间的实际球面距离,避免平面投影误差
- 数据量大时用空间索引(如KDTree)优化邻域点查询效率
- 边界情况处理:若单一点位7公里范围内人口总和不足5000,可选择保留为独立聚类或合并邻近小聚类(按需调整)
Python代码实现
1. 依赖库安装
pip install pandas numpy scipy
2. 完整代码
import pandas as pd import numpy as np from scipy.spatial import KDTree from math import radians, sin, cos, sqrt, atan2 # 计算两点经纬度的球面距离(单位:公里) def haversine(lat1, lon1, lat2, lon2): R = 6371.0 # 地球半径(公里) lat1_rad, lon1_rad = radians(lat1), radians(lon1) lat2_rad, lon2_rad = radians(lat2), radians(lon2) dlon = lon2_rad - lon1_rad dlat = lat2_rad - lat1_rad a = sin(dlat / 2)**2 + cos(lat1_rad) * cos(lat2_rad) * sin(dlon / 2)**2 c = 2 * atan2(sqrt(a), sqrt(1 - a)) return R * c # 带约束的空间聚类函数 def constrained_spatial_clustering(df, max_radius_km=7, min_population=5000): df['cluster_id'] = -1 # -1标记未聚类点 current_cluster = 0 while (df['cluster_id'] == -1).any(): # 取未聚类点中人口最多的作为聚类中心 unclustered = df[df['cluster_id'] == -1] center_idx = unclustered['population'].idxmax() center_lat, center_lon = unclustered.loc[center_idx, ['lat', 'lon']] # 计算所有未聚类点到中心的距离 unclustered['distance'] = unclustered.apply( lambda row: haversine(center_lat, center_lon, row['lat'], row['lon']), axis=1 ) # 筛选7公里范围内的候选点 candidate_points = unclustered[unclustered['distance'] <= max_radius_km] candidate_total_pop = candidate_points['population'].sum() # 满足人口阈值则生成聚类,否则标记为独立聚类 df.loc[candidate_points.index, 'cluster_id'] = current_cluster current_cluster += 1 return df # ------------------- 示例使用 ------------------- # 替换为你的真实数据集 sample_data = { 'lat': [31.2304, 31.2285, 31.2320, 31.1789, 31.1801], 'lon': [121.4737, 121.4705, 121.4750, 121.4850, 121.4862], 'population': [2500, 3000, 2000, 4000, 1500] } df = pd.DataFrame(sample_data) # 执行聚类 clustered_result = constrained_spatial_clustering(df) print(clustered_result)
性能优化(大数据量场景)
当数据量超过1万条时,用KDTree替代逐行计算距离可大幅提升效率:
# 预转换经纬度为弧度并构建KDTree coords = np.radians(df[['lat', 'lon']].values) kdtree = KDTree(coords) # 在聚类循环中替换距离计算逻辑 radius_rad = max_radius_km / 6371.0 # 公里转弧度 center_coords = np.radians([[center_lat, center_lon]]) neighbor_indices = kdtree.query_ball_point(center_coords, radius_rad)[0] candidate_points = df.loc[neighbor_indices]
内容的提问来源于stack exchange,提问作者Pallav Gupta
相关产品推荐
相关产品推荐

