通过实例浅析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数据结构之二叉树的统计与转换实例
Apr 29 Python
Python 'takes exactly 1 argument (2 given)' Python error
Dec 13 Python
python类的继承实例详解
Mar 30 Python
python实现kNN算法
Dec 20 Python
利用python 更新ssh 远程代码 操作远程服务器的实现代码
Feb 08 Python
python实现数据库跨服务器迁移
Apr 12 Python
python使用matplotlib绘制热图
Nov 07 Python
详解Python中的测试工具
Jun 09 Python
Python实现在线批量美颜功能过程解析
Jun 10 Python
PyCharm+PyQt5+QtDesigner配置详解
Aug 12 Python
如何用python绘制雷达图
Apr 24 Python
将Python代码打包成.exe可执行文件的完整步骤
May 12 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
解决phpmyadmin中文乱码问题。。。
2007/01/18 PHP
浅析php过滤html字符串,防止SQL注入的方法
2013/07/02 PHP
php中get_magic_quotes_gpc()函数说明
2017/02/06 PHP
PHP进程通信基础之信号量与共享内存通信
2017/02/19 PHP
PHP对象相关知识总结
2017/04/09 PHP
Thinkphp 在api开发中异常返回依然是html的解决方式
2019/10/16 PHP
PHP中类与对象功能、用法实例解读
2020/03/27 PHP
JavaScript 仿关机效果的图片层
2008/12/26 Javascript
JavaScript 提升运行速度之循环篇 译文
2009/08/15 Javascript
使用jquery实现简单的ajax
2013/07/08 Javascript
基于mouseout和mouseover等类似事件的冒泡问题解决方法
2013/11/18 Javascript
JS判断字符串包含的方法
2015/05/05 Javascript
JavaScript实现点击单选按钮改变输入框中文本域内容的方法
2015/08/12 Javascript
Angular2中Bootstrap界面库ng-bootstrap详解
2016/10/18 Javascript
xmlplus组件设计系列之文本框(TextBox)(3)
2017/05/03 Javascript
AugularJS从入门到实践(必看篇)
2017/07/10 Javascript
Bootstrap Table 删除和批量删除
2017/09/22 Javascript
Vue 中批量下载文件并打包的示例代码
2017/11/20 Javascript
JS实现对json对象排序并删除id相同项功能示例
2018/04/18 Javascript
vue实现一个炫酷的日历组件
2018/10/08 Javascript
layui按条件隐藏表格列的实例
2019/09/19 Javascript
小谈angular ng deploy的实现
2020/04/07 Javascript
[03:48]DOTA2完美大师赛主赛事第二日精彩集锦
2017/11/24 DOTA
Python模块学习 datetime介绍
2012/08/27 Python
Python获取网页上图片下载地址的方法
2015/03/11 Python
python中requests和https使用简单示例
2018/01/18 Python
pandas.DataFrame的pivot()和unstack()实现行转列
2019/07/06 Python
美国电视购物:QVC
2017/02/06 全球购物
采购内勤岗位职责
2013/12/10 职场文书
家长对孩子的感言
2014/03/10 职场文书
教师纪念9.18事件演讲稿范文
2014/09/14 职场文书
2014超市收银员工作总结
2014/11/13 职场文书
机关单位保密工作责任书
2015/05/11 职场文书
培训班开班主持词
2015/07/02 职场文书
简单谈谈Python面向对象的相关知识
2021/06/28 Python
python创建字典及相关管理操作
2022/04/13 Python