求助:优化Haversine距离矩阵计算,替代嵌套循环的高效实现方案
如何高效计算两组坐标间的Haversine距离矩阵(避免嵌套循环)
我现在用嵌套循环结合haversine库计算两组坐标的距离矩阵,虽然能跑,但处理大数据集时速度太慢。想找一种不用循环的高效替代方案,以下是当前代码:
import numpy as np from haversine import haversine string_list_1 = [(20.00,-100.1),...] # 第一组坐标列表(纬度, 经度) string_list_2 = [(21.00,-101.1),...] # 第二组坐标列表 dist_mat = np.zeros((len(string_list_1), len(string_list_2))) for i, coord1 in enumerate(string_list_1): dist_mat[i, :] = np.array([haversine(coord1, coord2) for coord2 in string_list_2])
求更高效、无循环的实现方式。
方案1:基于Numpy广播的全向量化实现(推荐)
核心思路是把坐标转换成Numpy数组,利用广播机制实现所有坐标对的批量计算,完全避开Python循环,底层用C执行计算,效率提升非常明显。
import numpy as np def haversine_vectorize(lat1, lon1, lat2, lon2): # 转弧度 lat1_rad = np.radians(lat1) lon1_rad = np.radians(lon1) lat2_rad = np.radians(lat2) lon2_rad = np.radians(lon2) # Haversine公式计算,通过维度扩展实现广播 dlat = lat2_rad - lat1_rad[:, np.newaxis] dlon = lon2_rad - lon1_rad[:, np.newaxis] a = np.sin(dlat / 2)**2 + np.cos(lat1_rad[:, np.newaxis]) * np.cos(lat2_rad) * np.sin(dlon / 2)**2 c = 2 * np.arcsin(np.sqrt(a)) # 地球半径(单位:公里,若要英里替换为3956) r = 6371 return c * r # 转换坐标为Numpy数组 coords1 = np.array(string_list_1) coords2 = np.array(string_list_2) # 直接生成距离矩阵 dist_mat = haversine_vectorize(coords1[:, 0], coords1[:, 1], coords2[:, 0], coords2[:, 1])
解释:
- 通过
[:, np.newaxis]将第一组坐标的维度从(N,)扩展为(N,1),与第二组的(M,)维度运算时,Numpy会自动广播为(N,M)的矩阵,一次性完成所有坐标对的计算。 - 全程无Python循环,所有运算在Numpy底层C代码执行,处理十万级坐标对也能快速完成。
方案2:用np.vectorize包装原haversine函数(快速改造)
如果不想自己实现Haversine公式,可以用np.vectorize把原标量函数包装成支持数组输入的函数,虽底层仍有循环,但比手动写Python循环更简洁高效:
import numpy as np from haversine import haversine # 包装成支持广播的向量化函数,指定输入输出签名 haversine_vec = np.vectorize(haversine, signature='(n),(n)->()') coords1 = np.array(string_list_1) coords2 = np.array(string_list_2) # 调整维度实现广播计算 dist_mat = haversine_vec(coords1[:, np.newaxis, :], coords2[np.newaxis, :, :])
注意:此方案效率略逊于方案1,因为np.vectorize只是语法糖,但胜在无需修改原函数逻辑,快速完成改造。
方案3:利用scipy的cdist函数(备选)
若环境已安装scipy,可以用scipy.spatial.distance.cdist结合自定义距离函数,代码更简洁:
import numpy as np from scipy.spatial.distance import cdist def haversine_dist(coord1, coord2): lat1, lon1 = coord1 lat2, lon2 = coord2 # Haversine公式实现 lat1_rad = np.radians(lat1) lon1_rad = np.radians(lon1) lat2_rad = np.radians(lat2) lon2_rad = np.radians(lon2) dlat = lat2_rad - lat1_rad dlon = lon2_rad - lon1_rad a = np.sin(dlat / 2)**2 + np.cos(lat1_rad) * np.cos(lat2_rad) * np.sin(dlon / 2)**2 c = 2 * np.arcsin(np.sqrt(a)) return 6371 * c coords1 = np.array(string_list_1) coords2 = np.array(string_list_2) # 生成距离矩阵 dist_mat = cdist(coords1, coords2, metric=haversine_dist)
cdist内部会优化计算流程,效率高于手动循环,但仍略低于方案1的纯Numpy广播实现。
内容的提问来源于stack exchange,提问作者PeCaDe
相关产品推荐
相关产品推荐

