python - 在 Python 中矢量化 Haversine 距离计算

标签 python performance numpy pandas vectorization

我正在尝试使用 Haversine 计算经纬度标识的一长串位置的距离矩阵采用两个坐标对元组来产生距离的公式:

def haversine(point1, point2, miles=False):
    """ Calculate the great-circle distance bewteen two points on the Earth surface.

    :input: two 2-tuples, containing the latitude and longitude of each point
    in decimal degrees.

    Example: haversine((45.7597, 4.8422), (48.8567, 2.3508))

    :output: Returns the distance bewteen the two points.
    The default unit is kilometers. Miles can be returned
    if the ``miles`` parameter is set to True.

    """

我可以使用嵌套的 for 循环计算所有点之间的距离,如下所示:

data.head()

   id                      coordinates
0   1   (16.3457688674, 6.30354512503)
1   2    (12.494749307, 28.6263955635)
2   3    (27.794615136, 60.0324947881)
3   4   (44.4269923769, 110.114216113)
4   5  (-69.8540884125, 87.9468778773)

使用一个简单的函数:

distance = {}
def haver_loop(df):
    for i, point1 in df.iterrows():
        distance[i] = []
        for j, point2 in df.iterrows():
            distance[i].append(haversine(point1.coordinates, point2.coordinates))

    return pd.DataFrame.from_dict(distance, orient='index')

但考虑到时间复杂性,这需要相当长的时间,大约需要 20 秒才能获得 500 个点,而且我有一个更长的列表。这让我开始关注矢量化,我遇到了 numpy.vectorize ( (docs) ,但无法弄清楚如何在这种情况下应用它。

最佳答案

来自 haversine's function definition ,它看起来非常可并行化。因此,使用最好的 NumPy 向量化工具之一,又名 broadcasting并将数学函数替换为 NumPy 等价物 ufuncs ,这是一个矢量化解决方案 -

# Get data as a Nx2 shaped NumPy array
data = np.array(df['coordinates'].tolist())

# Convert to radians
data = np.deg2rad(data)                     

# Extract col-1 and 2 as latitudes and longitudes
lat = data[:,0]                     
lng = data[:,1]         

# Elementwise differentiations for lattitudes & longitudes
diff_lat = lat[:,None] - lat
diff_lng = lng[:,None] - lng

# Finally Calculate haversine
d = np.sin(diff_lat/2)**2 + np.cos(lat[:,None])*np.cos(lat) * np.sin(diff_lng/2)**2
return 2 * 6371 * np.arcsin(np.sqrt(d))

运行时测试 -

其他np.vectorize based solution已经显示出与原始代码相比性能改进的一些积极 promise ,因此本节将比较基于发布广播的方法与那个方法。

函数定义-

def vectotized_based(df):
    haver_vec = np.vectorize(haversine, otypes=[np.int16])
    return df.groupby('id').apply(lambda x: pd.Series(haver_vec(df.coordinates, x.coordinates)))

def broadcasting_based(df):
    data = np.array(df['coordinates'].tolist())
    data = np.deg2rad(data)                     
    lat = data[:,0]                     
    lng = data[:,1]         
    diff_lat = lat[:,None] - lat
    diff_lng = lng[:,None] - lng
    d = np.sin(diff_lat/2)**2 + np.cos(lat[:,None])*np.cos(lat) * np.sin(diff_lng/2)**2
    return 2 * 6371 * np.arcsin(np.sqrt(d))

时间 -

In [123]: # Input
     ...: length = 500
     ...: d1 = np.random.uniform(-90, 90, length)
     ...: d2 = np.random.uniform(-180, 180, length)
     ...: coords = tuple(zip(d1, d2))
     ...: df = pd.DataFrame({'id':np.arange(length), 'coordinates':coords})
     ...: 

In [124]: %timeit vectotized_based(df)
1 loops, best of 3: 1.12 s per loop

In [125]: %timeit broadcasting_based(df)
10 loops, best of 3: 68.7 ms per loop

关于python - 在 Python 中矢量化 Haversine 距离计算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34502254/

相关文章:

python - 从 s3 读取 .pptx 文件

python - 提高 Python 嵌套 for 循环的性能

python - 连接系列中列表的元素

python - 为多个 xlsx 文件目录中的每个文件创建具有特定列总和的新工作表

python - 将非内存连续的 c/c++ 数据包装为 numpy 数组

python - 如何强制我的 virtualenv 看到这个编译模块安装在系统级别?

在 if 语句中使用 "not in"时出现 Python 错误?

python - Scrapy Spider 不提取 xpath 数据

java - 是否有任何有效和优化的方法来在 long[] 数组中存储 500M+ 元素?

c# - SQL Server Compact 与 C# 数据结构的比较