设为首页 加入收藏

TOP

Python 不是 C(一)
2015-08-31 21:24:10 来源: 作者: 【 】 浏览:68
Tags:Python

我一直使用 Python,用它处理各种数据科学项目。?Python 以易用闻名。有编码经验者学习数天就能上手(或有效使用它)。


听起来很不错,不过,如果你既用 Python,同时也是用其他语言,比如说 C 的话,或许会存在一些问题。


给你举个我自己经历的例子吧。 我精通命令式语言,如 C 和 C++。对古老经典的语言如?Lisp 和 ?Prolog 能熟练使用。另外,我也用过 Java,java script 和 PHP 一段时间。(那么,学习) Python 对我来讲不是很简单吗?事实上,只是看起来容易,我给自己挖了个坑:我像用 C 一样去用 Python。


具体情况,请向下看。


一个最近的项目中,需要处理地理空间数据。给出(任务)是 gps 追踪 25,000 个左右位置点,需要根据给定的经纬度,重复定位距离最短的点。我第一反应是,翻查(已经实现的)计算已知经纬度两点间距离的代码片段。代码可以在 John D. ?Cook 写的这篇?code?available?in?the?public?domain 中找得到。?


万事俱备! 只要写一段 Python 函数,返回与输入坐标距离最短的点索引(25,000 点数组中的索引),就万事大吉了:


def closest_distance(lat,lon,trkpts):
? ? d = 100000.0
? ? best = -1
? ? r = trkpts.index
? ? for i in r:
? ? ? ? lati = trkpts.ix[i,'Lat']
? ? ? ? loni = trkpts.ix[i,'Lon']
? ? ? ? md =?distance_on_unit_sphere(lat, lon, lati, loni)
? ? ? ? if d > md
? ? ? ? ? ? best = i
? ? ? ? ? ? d = md
? ? return best


其中,?distance_on_unit_sphere 是 John D. Cook's 书中的函数,trkpts 是数组,包含 gps 追踪的点坐标(实际上,是 pandas 中的数据帧,注,pandas 是 python 第三方数据分析扩展包)。?


上述函数与我以前用 C 实现的函数基本相同。 它遍历(迭代)trkpts 数组,将迄今为止(距离给定坐标位置)的距离最短的点索引值,保存到本地变量 best 中。?


目前为止,情况还不错,虽然 Python 语法与 C 有很多差别,但写这段代码,并没有花去我太多时间。


代码写起来快,但执行起来却很慢。例如,我指定428 个点,命名为waypoints(导航点,路点,导航路线中的关键点)。导航时,我要为每个导航点 waypoint 找出距离最短的点。为 428 个导航点 waypoint 查找距离最短点的程序,在我的笔记本上运行了 3 分 6 秒。


之后,我改为查询计算曼哈坦距离,这是近似值。我不再计算两点间的精确距离,而是计算东西轴距离和南北轴距离。计算曼哈坦距离的函数如下:


def manhattan_distance(lat1, lon1, lat2, lon2):
? ? lat = (lat1+lat2)/2.0
? ? return abs(lat1-lat2)+abs(math.cos(math.radians(lat))*(lon1-lon2))


实际上,我用了一个更简化的函数,忽略一个因素,即维度曲线上 1 度差距比经度曲线上的 1 度差距要大得多。简化函数如下:


def manhattan_distance1(lat1, lon1, lat2, lon2):
? ? return abs(lat1-lat2)+abs(lon1-lon2)


? ? closest 函数修改为:?


def closest_manhattan_distance1(lat,lon,trkpts):
? ? d = 100000.0
? ? best = -1
? ? r = trkpts.index
? ? for i in r:
? ? ? ? lati = trkpts.ix[i,'Lat']
? ? ? ? loni = trkpts.ix[i,'Lon']
? ? ? ? md =?manhattan_distance1(lat, lon, lati, loni)
? ? ? ? if d > md
? ? ? ? ? ? best = i
? ? ? ? ? ? d = md
? ? return best


如果将 Manhattan_distance 函数体换进来,速度还可以快些:


def closest_manhattan_distance2(lat,lon,trkpts):
? ? d = 100000.0
? ? best = -1
? ? r = trkpts.index
? ? for i in r:
? ? ? ? lati = trkpts.ix[i,'Lat']
? ? ? ? loni = trkpts.ix[i,'Lon']
? ? ? ? md =?abs(lat-lati)+abs(lon-loni)
? ? ? ? if d > md
? ? ? ? ? ? best = i
? ? ? ? ? ? d = md
? ? return best


在计算的最短距离点上,用这个函数与用 John's 的函数效果相同。我希望我的直觉是对的。越简单就越快。现在这个程序用了 2 分 37 秒。提速了 18%。?很好,但还不够激动人心。


我决定正确使用 Python。这意味着要利用 pandas 支持的数组运算。这些数组运算操作源于 numpy 包。通过调用这些数组操作,代码实现更简练:


def closest(lat,lon,trkpts):
? ? cl = numpy.abs(trkpts.Lat - lat) + numpy.abs(trkpts.Lon - lon)
? ? return cl.idxmin()


该函数与之前函数的返回结果相同。在我的笔记本上运行时间花费了 0.5 秒。整整快了 300 倍!? 300 倍,,也即30,000 %。不可思议。?提速的原因是 numpy 数组操作运算用 C 实现。因此, 我们将最好的两面结合起来了:?我们得到 C 的速度和 Python 的简洁性。


教训很明确:别用 C 的方式写 Python 代码。用 numpy 数组运算,不要用数组遍历。对我来说,这是思维上的转变。


Update on July 2, 2015。文章讨论在Hacker?News。一些评论没有注意到(missed )我用到了 pandas 数据帧的情况。主要是它在数据分析中很常用。如果我只是要快速的查询最短距离点,且我时间充分,我可以使用 C 或 C++ 编写四叉树(实现)。


Second update on July 2, 2015。有个评论提到?numba?也能对代码提速。我就试了一下。


这是我的做法,与你的情况不一定相同。 首先,要说明的是,不同的 python 安装版,实验的结果不一定相同。我的实验环境是 windows 系统上安装 Anaconda,同时也安装了一些扩展包。可能这些包和 numba 存在干扰。.?


首先,输入下面的安装命令,安装 numba:


这是我命令行界面上的反馈:


image


之后我发现,numba 在 anaconda 安装套件中已存在。?也可能安装指令有变更也说不定。


推荐的 numba 用法:


@jit
def closest_func(lat,lon,trkpts,func):
? ? d = 100000.0
? ? best = -1
? ? r = trkpts.index
? ? for i in r:
? ? ? ? lati = trkpts.ix[i,'Lat']
? ? ? ? loni = trkpts.ix[i,'Lon']
? ? ? ? md = abs(lat - lati) + abs(lon - loni)
? ? ? ? if d > md:
? ? ? ? ? ? #print d, dlat, dlon, lati, loni
? ? ? ? ? ? best = i
? ? ? ? ? ? d = md
? ? r

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇二叉树遍历算法总结(递归与非递.. 下一篇Java实现简单计算器源代码

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: