熊猫数据框中的每一行包含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'])
下面是同一函数的矢量化numpy版本:
输入都是值的数组,它应该能够立即完成数百万个点。要求输入是ndarrays,但是pandas表的列可以工作。
例如,对于随机生成的值:
或者如果要创建另一列:
在python中,遍历数据数组的速度非常慢。Numpy提供了对整个数据数组进行操作的函数,这样可以避免循环并显著提高性能。
这是vectorization的一个例子。
纯粹为了一个说明性的例子,我在@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和lons(以度为单位),但这对解释来说并不重要。
接下来我们需要做的是编译我们的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中测试它,剩下的就是生成一个size变量,以及一个数组,该数组将被修改(就像在C代码中一样)以包含结果数据,然后我们可以调用它:
把它们放在
haversine.py
的__main__
块中,整个文件现在看起来如下:要运行它,它将分别运行Python和
ctypes
版本并计时,并打印一些结果,我们可以显示:
正如预期的那样,
numpy
版本稍快(长度为100万的向量为0.11秒),但是我们快速而肮脏的ctypes
版本并不是没精打采的:在相同的数据上的0.148秒。我们将其与Python中的naive for循环解决方案进行比较:
当我将它放入与其他Python文件相同的Python文件中,并在相同的百万元素数据上计时时,我始终看到在我的机器上大约2.65秒。
因此,通过快速切换到
ctypes
,我们将速度提高了约18倍。对于许多可以从访问裸的、连续的数据中获益的计算,您经常会看到甚至比这更高的收益。非常清楚地说,我根本不赞同这是一个比使用
numpy
更好的选择。这正是构建numpy
要解决的问题,因此,无论何时(a)在应用程序中合并numpy
数据类型是有意义的,并且(b)存在一种将代码映射到numpy
等价物的简单方法,这都不是非常有效的。但是,如果您喜欢用C编写一些东西,却又用Python调用它,或者依赖于
numpy
是不实际的(例如,在无法安装numpy
的嵌入式系统中),那么了解如何这样做仍然非常有帮助。如果允许使用scikit learn,我将提供以下机会:
相关问题 更多 >
编程相关推荐