Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。

如果你用Python做过一些数据分析相关的项目,那么很有可能你已经接触过Pandas,由Wrs McKinney编写的超赞的数据分析库。通过向Python提供数据框(dataframe)分析功能,Pandas将Python推升到和一些成熟的数据分析工具如R和SAS相近的地位。

不幸的是,一开始Pandas就获得了运行特别慢的名声。必须得承认,你的Pandas代码不太可能达到例如完全由C源码优化的代码的执行速度。好消息是对大多数的应用场景来说,经过足够优化的Pandas代码已经足够快速;Pandas在速度上的缺陷,则由其功能的强大和对用户的友好所弥补。

本文中,我们将按由慢到快的顺序回顾将函数运用到Pandas数据框的几种方法的效率:

1、直接使用索引直接遍历数据框

2、使用iterrows()进行循环

3、使用apply()循环

4、Pandas序列的矢量化

5、NumPy链表的矢量化

我们将使用Haversine(或者Great Circle)距离公式作为测试函数。该函数取两点的经纬度作为参数,考虑地球表面的曲率,计算两点之间的直线距离。函数看起来大概是下面这个样子:

我们使用了包含纽约所有旅店真实经纬度的数据集来测试该函数,数据来源于Expedia’s developer site。我们将会计算每个旅店和一组样本坐标(刚好属于纽约的一个很有意思的名为Brooklyn Superhero Supply Store的小商店)的距离。

你可以直接下载数据集,另外Jupyter notebook包含了本博客中用到的函数,点这里下载。

本文基于我在PyCon上做的演讲修改而来,点这里可以观看。

直接通过索引循环,

首先,对Pandas的数据结构基础进行快速回顾。Pandas的基础结构可以分为两种:数据框和序列。数据框是拥有轴标签的二维链表,换言之数据框是拥有标签的行和列组成的矩阵 - 列标签位列名,行标签为索引。Pandas中的行和列是Pandas序列 - 拥有轴标签的一维链表。

基本上我接触过的所有Pandas初学者(当然也包括你们),在某个时刻都会尝试通过遍历将某个固定函数循环应用到数据框的每一行。这种方法的优势在于保持了和Python可迭代对象交互的一致性;例如,遍历列表或者元组的方法。相反,该种方法的缺陷在于,在Pandas中直接使用循环的速度是最慢的。和后面我们提到的方法相比,在Pandas中直接进行循环根本没有使用到任何内置的优化,相比之下显得效率极低,而且通常可读性还更差。

一种常被用到的方法大概是这么写的:

为了对上述函数的执行时间有一个直观的感觉,我们使用%timeit命令。%timeit 是Jupyter notebooks的一个魔法命令。(使用单个 % 开头的魔法命令作用于单行,而 %% 开头的命令则作用于整个Jupyter格)。%timeit 将会重复执行函数多次,并打印其接收到的平均运行时间及其标准差。当然,运行函数的系统不同,%timeit 接收的运行时间也不会完全相同。但是,它提供了一个在相同系统相同数据集上比较不同函数运行时间的基准测试工具。

该命令得到以下结果:

直接进行遍历的函数每次运行需要645毫秒,标准差为31毫秒。看上去还挺快,但考虑到该函数仅仅作用于1600行,这个速度其实挺慢了。还是接着看看我们如何提升这个情况吧。

使用iterrows()方法进行循环

如果必须使用循环来遍历所有行的话,还有一种更好的办法,那就是使用iterrows()方法。iterrows()是在数据框中的行进行迭代的一个生成器,它返回每行的索引及一个包含行本身的对象。iterrows() 针对Pandas的数据框进行了优化,即便和我们后面提到的几种标准函数相比效率相对较差,但和上面直接进行循环相比已经有了显著提升。同样的功能,iterrows() 的速度差不多为直接在行上进行循环的4倍。

使用apply方法更好得进行循环

比iterrows() 更好的操作是使用apply() 方法,它实现了将函数应用于数据框的特定轴(行、列皆可)。尽管apply() 使用的也是在行之间循环的思路,但由于利用了类似Cython的迭代器的一系列全局优化,其效率要比iterrows()高很多。

可以使用匿名的lambda函数将Haversine函数应用于每一行,该方法将每行的特定区域作为函数的输入参数。lambda函数的末尾包含axis参数,用来告知Pandas将函数运用于行(axis = 1)或者列(axis = 0)。

使用apply() 代替iterrows()节省了近一半的函数运行时间。

为了更进一步搞清楚函数执行过程中的时间开销,我们可以使用line profiler tool(Jupyter中的 %lprun魔法命令)。

结果如下:

上述结果中,我们可以得到非常多有用的信息。例如,函数中执行三角函数的运算占用了接近一半的运行时间。因此,如果我们想要对函数的某个部分进行优化,就可以从这里入手了。目前,更值得注意的是每一行代码都被执行了1631次——这也是apply()方法在每一行迭代的结果。如果我们可以将重复执行的总量削减下来,总的运行时间也会随之而减少。这也正是接下来谈到的矢量化提升效率的地方。

Pandas series 的矢量化

为了搞清楚如何减少函数迭代执行的总量,我们需要回顾一下Pandas、DataFrame、series的基础单元,以上三种数据结构均基于链表。基础单元的固有结构直接被以整个链表作为参数的函数所调用,而不用按顺序执行每个值(被称为标量)。矢量化即在整个链表上进行操作的过程。

Pandas包括了非常丰富的矢量化函数库:从数学聚合运算到字符串函数(更多可用函数请查看Pandas文档)。内置的函数针对Pandas series和DataFrame进行了特殊优化。结果就是,使用Pandas的矢量化函数几乎都比完成类似目标自定义编写的循环更胜一筹。

截止到目前,我们做的仅仅是吧标量传给Haversine函数。然而,所有Haversine函数中用到的函数也都可以在链表上进行操作。这就使得对距离函数的矢量化变得非常简单:不同于以上的直接将经纬度标量传递给函数,我们直接把整个series(列)作为参数传递。这样做Pandas将充分利用矢量化函数可用的优化,尤其是同时对整个链表进行所有计算。

使用矢量化函数,我们的效率相对apply()方法提升了50倍,对iterrows()方法提升了100倍 —— 仅仅是修改了输入类型。

接下来看看函数是怎样执行的:

可以看到,apply() 方法执行了1631次函数,而矢量化版本仅仅执行了一次,因为它是同时作用于整个序列的。这也正是矢量化可以节省如此多的时间的原因。

Numpy arrays的矢量化

到这里,其实已经满足我们的日常需求了;Pandas series的矢量化已经实现了日常计算所需要的绝大部分优化。然而,如果对速度的优先级要求很高,我们可以调用NumPy库的形式进一步优化。

NumPy,自我描述为“Python科学计算的基础包”,使用预编译的C代码在底层进行优化。和Pandas类似,NumPy也是在链表对象上进行操作(被称为ndarrays);不同的是,它避免了Pandas series操作过程中的很多开销,例如索引、数据类型等等。因此,NumPy arrays的操作要比Pandas series快得多。

当Pandas series提供的额外功能不是必须的时候,可以使用NumPy arrays代替Pandas series。例如,我们Haversion函数的矢量化实现实际上并没有用到经纬度series的索引,因此没有这些索引也不会导致函数中断。相反,如果我们的操作中涉及类似DataFrame 拼接,就需要根据索引引用值,这种情况下就只有使用Pandas对象了。

使用values 方法可以很容易将经纬度链表从Pandas series转换为NumPy arrays。就像处理Pandas series类似,直接把NumPy array传给函数将使Pandas在整个矢量应用函数。

在NumPy array上运行速度又提升了四倍。总而言之,我们把运行时间从一开始使用循环的接近半秒钟,通过在NumPy上使用矢量化方法减少到三分之一毫秒!

总结

下面的计分板总结了所有结果。尽管NumPy arrays的矢量化运行速度最快,其边际提升相比Pandas series的矢量化还是略显平缓,相比最快的循环Pandas series矢量化速度提升达到56倍。

对Pandas代码进行优化的一些总结如下:

1、避免使用循环;循环的执行速度慢,有时候还很不必要;

2、不得不使用循环时,使用apply() 方法,而不是循环调用函数;

3、矢量化比使用标量操作更好。Pandas中大多数的操作都可以被矢量化。

4、NumPy arrays上的矢量化操作要比Pandas series更高效。

当然,以上几点并不是对Pandas进行优化的完整列表。例如,更激进的用户可能会考虑,使用重写Cython中的函数,或者尝试优化函数的各个部分。但这些就已经超出本文讨论的范畴了。

最重要的是,在开启一次庞大的优化冒险旅程之前,先确保你打算优化的函数确实是之后会长期使用的函数。引用xkcd不朽的名言:“过早优化是万恶之源Premature optimization is the root of all evil。”

编者勘误:有读者指出“premature optimization” 这句引用的原作者并不是xkcd,而是Donald Knuth。我对这个遗漏道歉,恳请那位读者原谅。

想要和Sofia(作者)讨论Pandas么?来看看我们的工程师职位,从今天起和Sofia在Upside一起工作吧:http://upsd.io/2vavhrI。英文原文:https://ogmcsrgk5.qnssl.com/vcdn/1/优质文章长图/a-beginners-guide-to-optimizing-pandas-code-for-speed-c09ef2c6a4d6.png

译者:woody

python.freelycode.com-优化Pandas代码执行速度入门指南相关推荐

  1. 用STRAIGHT_JOIN优化mysql的执行速度

    优化sql的执行速度,我们通常需要查看EXPLAIN的结果,没有使用索引的加上索引,索引使用不对的也可以强制使用某个索引,但是有些情况下,这2招都不管用,是sql内部的执行顺序不正确,导致速度很慢. ...

  2. python中函数提高代码执行速度吗_为什么Python代码在一个函数中运行得更快?

    匿名用户 除了局部/全局变量存储时间外,操作码预测使函数更快. 正如其他答案所解释的,该函数在循环中使用store_fast操作码.下面是函数循环的字节码:>> 13 FOR_ITER 6 ...

  3. ESP32S3系列--代码执行速度优化(从PSRAM执行代码)

    一.目的 在上一篇博文中我们介绍了ESP32S3的Flash/PSRAM的一些配置,包括时钟.IO模式(单线.双线.四线.八线).采样模式(SDR/DDR) ESP32S3系列--FLASH及PSRA ...

  4. php调用python pkl_Python Pickle的任意代码执行漏洞实践和Payload构造

    *原创作者:bit4@勾陈安全实验室,MottoIN原创文章未经许可禁止转载 0x01 Pickle的典型应用场景 一般在什么场景下需要用到Pickle?通常在解析认证token,session的时候 ...

  5. 优化SQL的执行速度

    2019独角兽企业重金招聘Python工程师标准>>> 在项目开发中,页面的反应速度是非常重要的,改善页面反应速度的方法有很多. 但一般的问题多数是出现在数据库访问的SQL上面. 比 ...

  6. Yamale Python 包受高危的代码执行漏洞影响

     聚焦源代码安全,网罗国内外最新资讯! 编译:代码卫士 YAML 的机构和验证工具 Yamale 中存在一个高危的代码注入漏洞,可被攻击者用于执行任意 Python 代码. 该漏洞的编号是CVE-20 ...

  7. c++ 界面交互影响处理代码执行速度_原创 | 某SCADA的远程代码执行漏洞挖掘与利用...

    作者 | 绿盟科技格物实验室 陈杰 前言 近年来网络安全形势日渐严峻,国内外都开始对工控安全越来越重视,而工控领域由于常年来对安全的忽视,导致暴露出数量惊人的严重安全漏洞,更为严重的是,相当一部分厂商 ...

  8. python语音识别_Python中的语音识别-完整的入门指南

    python语音识别 Welcome to The Complete Beginner's Guide to Speech Recognition in Python. 欢迎使用Python语音识别完 ...

  9. mysql怎么跑代码_MySQL菜鸟入门指南_mysql

    mysql是完全网络化的跨平台关系型数据库系统,一个真正的多用户.多线程SQL数据库服务器,同时是具有客户机/服务器体系结构的分布式数据库管理系统.它具有功能强.使用简便.管理方便.容易使用.运行速度 ...

最新文章

  1. 一维二维码的提取、识别和产生
  2. python3.7[列表] 索引切片
  3. 【BZOJ3590】[Snoi2013]Quare 状压DP
  4. Kafka 优化参数 unclean.leader.election.enable
  5. php rsa加密乱码_PHP RSA密文过长加密解密 越过1024的解决代码
  6. CCNA学习要点,希望能对初学者有帮助
  7. 国际著名密码学专家来学嘉:区块链对密码学的两大贡献
  8. c++ hashset的用法_C++ set crbegin() 使用方法及示例
  9. lisp 标注螺纹孔_cad中螺纹孔怎么标注
  10. VS2010+VAX+CUDA6.0配置语法高亮和自动补充
  11. JavaScript用法------判断二维数组
  12. php curl添加授权Authorization 验证头
  13. 25年后的晶体管会是什么样?
  14. 灵感 | 设计平平无奇?试试这种方法!
  15. js文字转图片,使用画布绘制
  16. 刘知远 | 语义表示学习
  17. 计算机毕业设计ssm财务报账管理系统l6tbn系统+程序+源码+lw+远程部署
  18. DLL 导入的两种方式
  19. linux 软ap 设备数量,Linux下软AP功能之Hostapd介绍
  20. C++条形码生成器Aspose.BarCode for C++更新至v19.5 | 附下载

热门文章

  1. 怎样快速查看文档是否统一_开发到底喜欢看怎样的需求文档
  2. Android 机型适配之gradient默认渐变方向
  3. 内网使用ohmyzsh
  4. 趣谈网络协议笔记-二(第十八讲)
  5. UEBA 学术界研究现状——用户行为异常检测思路:序列挖掘prefixspan,HMM,LSTM/CNN,SVM异常检测,聚类CURE算法...
  6. Proxy代理 和 Reflect反射(反射的是obj)的概念
  7. C#调用Mail发送QQ邮件
  8. Django-admin管理工具
  9. 2015.7.16(小高开忍住没有减仓,大盘涨3.5%,百股涨停——买进中重、中航,指导WXL错误)...
  10. 前端——JavaScript