学会使用Pandas,代码运行速度提高9.39倍
此panda非彼Pandas(汗~)
Pandas库对数据科学界来说是一份天赐的礼物。如果问大多数数据科学家喜欢如何处理Python里的数据集,几乎毫无疑问都会提及Pandas。
Pandas是一个浩大编程库的缩影:简单、直观、功能广泛。
而对于数据科学家来讲,进行一项常规工作——使用Pandas库中的 dataframe进行数千甚至数百万次的计算仍是一个挑战。这并非只是投入数据,还包括编写Python for循环语句,然后希望数据在合理的时间内被处理。
Pandas是为一次性处理整个行或列的矢量化操作而设计的——循环遍每个单元格、行或列,而并非是库的设计用途。因此当与Pandas一起工作时,应考虑到可高度并行的矩阵运算。
图片来源:pexels.com
本指南将教会如何使用和思考Pandas并用其设计矩阵运算。在此过程中,将展示一些实用的节省时间的技巧,以使Pandas代码运行得比那些可怕的Python for循环快得多!
设置
本教程将使用经典的鸢尾花数据集。从使用seaborn载入数据集并打印出前5行来开始吧。
太棒啦!
现在建立一条基线,用Python for循环来测量速度。通过循环遍历每一行来设置要在数据集上进行的计算,再测量整个操作的速度。由此得到的基线将看出新的优化能在多大程度上提供帮助。
在上面的代码中创建了一个基本函数,用If-Else语句根据花瓣长度选择花朵类别。编写了一个for循环语句,通过循环dataframe对每一行应用该基本函数,再测量循环的总运行时。
在i7–8700k机器上,循环运行5次平均需要0.01345秒。
循环与.iterrows ()
使用Pandas的内置.iterrows()函数可提升代码运行速度,该函数虽简单,但却十分有价值。
上部分编写for循环使用了range()函数。然而当在Python中对大范围的值进行循环时,生成器的速度往往要快得多。在本文中here可以阅读更多关于生成器如何工作,并使其运行得更快的文章。
Pandas的.iterrows()函数在内部实现了一个生成器函数,将在每次迭代中 yield一行dataframe。更准确地说,.iterrows()为DataFrame中的每一行生成(index, Series)的一对(元组)。这实际上与在原始Python中使用enumerate()之类的东西是一样的,但运行速度要快得多。
下面修改了代码,使用.iterrows()代替常规的for循环。在上部分测试所用的同一台机器上,平均运行时间为0.005892秒——提高了2.28倍!
使用.apply()完全删除循环
iterrows()函数极大地提高了速度,但还远远没有完成。始终记住当使用为向量操作设计的库时,可能有一种方法可以在完全没有for循环的情况下最高效地完成任务。
提供这种功能的是Pandas的.apply()函数。其接受另一个函数作为输入,并沿着DataFrame的轴(行、列等)应用。在传递函数情况下,lambda通常可以方便地将所有内容打包在一起。
下面的代码已经完全用.apply()和lambda函数替换了for循环,以打包所需的计算。在机器上这段代码的平均运行时间是0.0020897秒——比原来的for循环快6.44倍。
.apply() 之所以快得多,是因为内部尝试循环 Cython迭代器。若函数恰好为Cython优化得很好,.apply()将使速度更快。额外好处是使用内置函数可以生成更干净可读的代码。
总结
上述提到若正在使用一个为向量化操作设计的库,应该找到一种不使用for循环进行任何计算的方法。
类似许多以这种方式设计的库,包括Pandas,具有方便的内置函数,可以执行想要的精确计算—且速度更快。
一组用If-Else定义各个范围的 bins 值和在对应各个范围返回的 labels值作为Pandas的.cut()函数的输入。接着执行与compute_class()函数手动编写完全相同的操作。
用下面的代码查看.cut()是如何运作的。使用干净可读的代码使程序的运行速度得到了很大的提升。最后,.cut()函数的平均运行时间为0.00142L3秒——比原来的for循环快了9.39倍!
留言 点赞 关注
我们一起分享AI学习与发展的干货
欢迎关注全平台AI垂类自媒体 “读芯术”