Pandas坐标框中的每一行包含2个点的纬度/纬度坐标。使用下面的Python代码,计算许多(数百万)行的这两个点之间的距离需要很长时间!
考虑到这两个点相距不到50英里,准确性不是很重要,有没有可能使计算更快?
from math import radians, cos, sin, asin, sqrt
def haversine(lon1, lat1, lon2, lat2):
"""
Calculate the great circle distance between two points
on the earth (specified in decimal degrees)
"""
# convert decimal degrees to radians
lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
# haversine formula
dlon = lon2 - lon1
dlat = lat2 - lat1
a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
c = 2 * asin(sqrt(a))
km = 6367 * c
return km
for index, row in df.iterrows():
df.loc[index, 'distance'] = haversine(row['a_longitude'], row['a_latitude'], row['b_longitude'], row['b_latitude'])
7条答案
按热度按时间du7egjpx1#
下面是同一函数的向量化numpy版本:
输入都是值的数组,它应该能够立即处理数百万个点。要求是输入是ndarrays,但pandas表的列将工作。
例如,对于随机生成的值:
如果你想创建另一个列:
在python中循环遍历数据数组是非常慢的。Numpy提供了对整个数据数组进行操作的函数,可以避免循环并大幅提高性能。
这是一个vectorization的例子。
uurv41yg2#
为了便于说明,我在@ballsdotballs的答案中采用了
numpy
版本,并通过ctypes
调用了一个配套的C实现。由于numpy
是一个高度优化的工具,所以我的C代码几乎不可能有同样的效率,但它应该有点接近。这里最大的优点是,通过运行一个C类型的示例,它可以帮助您了解如何在没有太多开销的情况下将您自己的个人C函数连接到Python。当你只想优化一个更大的计算中的一小部分时,这是特别好的,通过在一些C源代码而不是Python中编写这一小部分。大多数情况下,简单地使用numpy
就可以解决这个问题,但是对于那些实际上并不需要所有numpy
的情况,并且您不想添加耦合以要求在某些代码中使用numpy
数据类型的情况,知道如何下拉到内置的ctypes
库并自己完成是非常方便的。首先,让我们创建一个C源文件,名为
haversine.c
:请注意,我们试图保持与C公约。通过引用解释性地传递数据参数,使用
size_t
作为大小变量,并期望我们的haversine
函数通过改变其中一个传递的输入来工作,以便它在退出时包含预期的数据。该函数实际上返回一个整数,这是一个成功/失败标志,可以由该函数的其他C级消费者使用。我们需要找到一种方法来处理Python中所有这些C语言特有的小问题。
接下来,让我们将
numpy
版本的函数沿着一些导入和一些测试数据放入一个名为haversine.py
的文件中:我选择了在0到50之间随机选择的lats和隆恩(以度为单位),但这对这个解释来说并不重要。
我们需要做的下一件事是编译我们的C模块,使它可以被Python动态加载。我使用的是Linux系统(你可以在Google上很容易地找到其他系统的例子),所以我的目标是将
haversine.c
编译成一个共享对象,如下所示:我们也可以编译成一个可执行文件,然后运行它,看看C程序的
main
函数显示了什么:现在我们已经编译了共享对象
haversine.so
,我们可以使用ctypes
在Python中加载它,我们需要提供文件的路径来执行此操作:现在
haversine_lib.haversine
的行为就像一个Python函数,除了我们可能需要做一些手动类型封送处理,以确保输入和输出被正确解释。numpy
实际上提供了一些很好的工具,我在这里使用的是numpy.ctypeslib
。我们将构建一个 * 指针类型 *,它允许我们将numpy.ndarrays
传递给这些ctypes
加载的函数,就像它们是指针一样。代码如下:请注意,我们告诉
haversine_lib.haversine
函数代理根据我们想要的类型解释其参数。现在,为了测试它 * 从Python* 剩下的就是做一个大小变量,和一个数组,它将被变异(就像在C代码中一样)以包含结果数据,然后我们可以调用它:
将它们放在
haversine.py
的__main__
块中,整个文件现在看起来像这样:要运行它,它将分别运行和计时Python和
ctypes
版本,并打印一些结果,我们可以这样做,其显示:
正如预期的那样,
numpy
版本稍微快一点(长度为100万的向量为0.11秒),但我们的快速和肮脏的ctypes
版本并不懒散:0.148秒的数据。让我们将其与Python中的朴素for循环解决方案进行比较:
当我把它放入与其他文件相同的Python文件中,并对相同的百万元素数据进行计时时,我在机器上始终看到大约2.65秒的时间。
因此,通过快速切换到
ctypes
,我们将速度提高了约18倍。对于许多可以从访问裸连续数据中获益的计算,您经常会看到比这更高的收益。只是要超级清楚,我并不是在所有赞同这是一个更好的选择,而不仅仅是使用
numpy
。这正是numpy
要解决的问题,因此,无论何时(a)在应用程序中合并numpy
数据类型是有意义的,(b)存在一种简单的方法将代码Map到numpy
等价物,自制自己的ctypes
代码都不是很有效。但是,当您喜欢用C编写但用Python调用某些内容时,或者依赖
numpy
不切实际的情况下(例如,在无法安装numpy
的嵌入式系统中),了解如何做到这一点仍然非常有帮助。egdjgwm83#
如果允许使用scikit-learn,我会给予以下机会:
mm9b1k5b4#
对@derricw的矢量化解决方案的一个简单扩展,你可以使用
numba
将性能提高2倍,而几乎不需要改变你的代码。对于纯数值计算,这可能应该用于基准/测试,而不是可能更有效的解决方案。基准测试与Pandas函数:
完整的基准测试代码:
trnvg8h35#
vectorized函数指定“所有参数必须长度相等”。通过扩展“较大”数据集的边界,根据this,可以有效地找到所有i,j对元素的距离。
g9icjywg6#
Scikit-learn库还有另一个计算半正矢距离的函数,称为
haversine_distances
函数,可用于计算两个坐标之间的距离,参见下面的示例:gmol16397#
这些答案中的一些是地球半径的“圆化”。如果您将这些功能与其他距离计算器(如 geopy)进行比较,则这些功能将关闭。
如果你想要以英里为单位的答案,你可以把
R=3959.87433
换成下面的转换常数。如果你想要公里数,使用
R= 6372.8
。