通过实例浅析Python对比C语言的编程思想差异


Posted in Python onAugust 30, 2015

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

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

给你举个我自己经历的例子吧。 我精通命令式语言,如 C 和 C++。对古老经典的语言如 Lisp 和 Prolog 能熟练使用。另外,我也用过 Java,Javascript 和 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:

$ conda install numba

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

通过实例浅析Python对比C语言的编程思想差异

之后我发现,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
  return best

我没有发现运行时间提高。我也尝试了更积极的编译参数设置:

@jit(nopython=True)
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
  return best

这次运行代码时,出现一个错误:

通过实例浅析Python对比C语言的编程思想差异

看来,pandas 比 numba 处理代码更智能。

当然,我也能花时间修改数据结构,使 numba 能正确编译(compile)。可是,我为什么要这么干呢? 用 numpy 写的代码运行的足够快了。反正,我一直在用 numpy 和 pandas 。为什么不继续用呢?

也有建议我用pypy。这当然有意义,不过…我用的是托管服务器上的 Jupyter notebooks(注,在线浏览器的 python 交互式开发环境)。我用的是它提供的 python 内核,也即,官方的(regular)Python 2.7.x 内核。并没有提供 Pypy 选择。

也有建议用 Cython。好吧,如果我回头要编译代码 ,那我干脆直接用 C 和 C++ 就好了。我用 python,是因为,它提供了基于 notebooks(注:网页版在线开发环境)的交互式特性,可以快速原型实现。这却不是 Cython 的设计目标。

Python 相关文章推荐
Python用GET方法上传文件
Mar 10 Python
Python实现抓取城市的PM2.5浓度和排名
Mar 19 Python
python自动zip压缩目录的方法
Jun 28 Python
使用Python脚本生成随机IP的简单方法
Jul 30 Python
python socket多线程通讯实例分析(聊天室)
Apr 06 Python
Python下载网络小说实例代码
Feb 03 Python
使用python验证代理ip是否可用的实现方法
Jul 25 Python
Django压缩静态文件的实现方法详析
Aug 26 Python
Python3开发实例之非关系型图数据库Neo4j安装方法及Python3连接操作Neo4j方法实例
Mar 18 Python
用python实现名片管理系统
Jun 18 Python
详解python metaclass(元类)
Aug 13 Python
Python OpenCV之常用滤波器使用详解
Apr 07 Python
使用Python脚本将文字转换为图片的实例分享
Aug 29 #Python
Python中常见的数据类型小结
Aug 29 #Python
深入解析Python中的lambda表达式的用法
Aug 28 #Python
两个使用Python脚本操作文件的小示例分享
Aug 27 #Python
简介二分查找算法与相关的Python实现示例
Aug 26 #Python
使用Python的Bottle框架写一个简单的服务接口的示例
Aug 25 #Python
栈和队列数据结构的基本概念及其相关的Python实现
Aug 24 #Python
You might like
php伪静态之APACHE篇
2014/06/02 PHP
PHP中多维数组的foreach遍历示例
2014/06/13 PHP
php从数据库查询结果生成树形列表的方法
2015/04/17 PHP
PHP生成和获取XML格式数据的方法
2016/03/04 PHP
PHP微信刮刮卡 附微信接口
2016/07/22 PHP
Expandable "Detail" Table Rows
2007/08/29 Javascript
用Javascript同时提交多个Web表单的方法
2009/12/26 Javascript
jQuery Mobile页面返回不需要重新get
2016/04/26 Javascript
jQuery中animate的几种用法与注意事项
2016/12/12 Javascript
JS中Array数组学习总结
2017/01/18 Javascript
Node.js命令行/批处理中如何更改Linux用户密码浅析
2018/07/22 Javascript
JavaScript 高性能数组去重的方法
2018/09/20 Javascript
改变layer confirm弹窗按钮的颜色方法
2019/09/12 Javascript
element-ui 远程搜索组件el-select在项目中组件化的实现代码
2019/12/04 Javascript
微信小程序基于movable-view实现滑动删除效果
2020/01/08 Javascript
extjs图表绘制之条形图实现方法分析
2020/03/06 Javascript
vue中实现点击变成全屏的多种方法
2020/09/27 Javascript
python自动安装pip
2014/04/24 Python
使用Python的PEAK来适配协议的教程
2015/04/14 Python
Python操作SQLite数据库的方法详解
2017/06/16 Python
Python实现pdf文档转txt的方法示例
2018/01/19 Python
Python装饰器语法糖
2019/01/02 Python
对python字典过滤条件的实例详解
2019/01/22 Python
Python中整数的缓存机制讲解
2019/02/16 Python
Python 实用技巧之利用Shell通配符做字符串匹配
2019/08/23 Python
Python *args和**kwargs用法实例解析
2020/03/02 Python
美国批发零售网站:GearXS
2016/07/26 全球购物
香港万宁官方海外旗舰店:香港健与美连锁店
2018/09/27 全球购物
艺术系大学生毕业个人自我评价
2013/09/19 职场文书
元旦晚会主持词
2014/03/24 职场文书
销售个人求职信范文
2014/04/28 职场文书
对外汉语专业大学生职业生涯规划范文
2014/09/13 职场文书
检讨书范文500字
2015/01/28 职场文书
升学宴家长致辞
2015/07/27 职场文书
公务员岗前培训心得体会
2016/01/08 职场文书
Golang Elasticsearches 批量修改查询及发送MQ
2022/04/19 Golang