最近做科研时经常需要遍历整个DataFrame,进行各种列操作,例如把某列的值全部转成pd.Timestamp格式或者将某两列的值进行element-wise运算之类的。大数据的数据量随便都是百万条起跳,如果只用for循环慢慢撸,不仅浪费时间也没效率。在一番Google和摸索后我找到了遍历DataFrame的至少8种方式,其中最快的和最慢的可以相差12000倍!
本文以相加和相乘两种操作为例,测试8种方法的运行速度,并附上示范代码。
测试环境
Macbook Pro Retina with TouchBar (13inch, 2018) i5 8GB 512GB
OS: macOS Catalina 10.5.2
Python 3.7.5 (default, Nov 1 2019, 02:16:23)
[Clang 11.0.0 (clang-1100.0.33.8)] on darwin
示范用数据
本来想造100万笔的,但是100万笔跑%timeit要跑很久,最后造了3000笔,己经足以体现运行速度差异。为了避免快取影响,每个子实验进行前都会用这个代码重造数据。
import
实验1 - 两列元素相加
# aaa + bbb
实验1 结果
df
实验2 - 两列元素相乘
# aaa * bbb
实验2 结果
df
速度比较
我把结果可视化,方便比较运行速度。(顺便练习画图)
可以看到最快的数组操作和最慢的for+iloc相比差了将近万倍,是秒级和微秒级的差别。所有方法运行速度大小关系如下(由慢至快):for循环+iloc < pd.iterrows < for循环+at < pd.apply pd列表构造 = np列表构造 < pd数组操作 < np数组操作。pd和np列表构造的速度几乎一样,np列表构造略快一些(1.15毫秒和1.09毫秒的差别),所以实验只做pd列表构造。
把两个秒级操作去掉,详细地比较一下
列表构造把除了数组操作以外的其他方法按在地上磨擦,数组操作把列表构造按在地上磨擦。值得注意的是,for循环+iat的组合比pandas提供的最快遍历方法apply快40%左右,也就是说就算不懂apply的用法,只要把loc/iloc改成at/iat,依然可以有明显的提速。另外,DataFrame的栏位很多的时候,apply_limit方法其实会比对对整个数据框apply快很多(因为不用每次读取整个数据框),只是示范数据的栏位不多所以在这里显现不出差异。
pandas的数组操作和numpy的数组操作单独对比:
列表构造的运行速度是毫秒级的,数组操作是微秒级,np数组操作是pd数组操作的两倍。
作图代码
x
结论
优先使用numpy数组操作!不能数组操作的时候用列表构造!
能用at/iat就不用loc/iloc,能用apply就不用迭代,能用数组操作就不用其他方法。
运行速度:np数组操作 > pd数组操作 >>> np列表构造 = pd列表构造 >>> for循环+at > pd(分片).apply > pd.apply >>> pd.iterrows > for循环+iloc
关于at/iat和loc/iloc的速度比较,请见参考资料[4]。
参考资料
- How To Make Your Pandas Loop 71803 Times Faster
- rain润:10分钟python图表绘制 | seaborn入门(二):barplot与countplot
- seaborn.barplot
- 老狼:简单两步,大幅提高python数据处理速度
- seaborn.plotting_context