Pandas一直是数据科学界的天赐之物。询问任何数据科学家他们喜欢如何在Python中处理他们的数据集,他们无疑会谈论Pandas。Pandas是优秀编程库的缩影:简单、直观且用途广泛。然而,在Pandas中执行数千甚至数百万次计算,这是数据科学家的日常任务,仍然是一个挑战。您不能只是将数据放入,编写一个Pythonfor循环,并期望在合理的时间内处理数据。Pandas专为一次性处理整行或整列的矢量化操作而设计——循环遍历每个单元格、行或列并不是该库的设计目的。因此,在使用Pandas时,您应该考虑矩阵运算是高度可并行化的。本指南将教您如何按照设计用于矩阵运算的方式使用Pandas。在此过程中,我将向您展示一些实用的省时技巧和窍门,它们将使您的Pandas代码运行得比那些可怕的Pythonfor循环快得多!设置在本教程中,我们将使用经典的鸢尾花数据集。我们首先使用seaborn加载数据集并打印出前5行。现在让我们建立一个基线并使用Pythonfor循环测量我们的速度。我们将通过遍历每一行来设置要在数据集上执行的计算,然后测量整个操作的速度。这将为我们提供一个基准,以了解我们的新优化在多大程度上帮助我们加快了速度。在上面的代码中,我们创建了一个基本函数,它使用If-Else语句根据花瓣的长度选择花的类别。我们编写了一个for循环,通过遍历数据帧将此函数应用于每一行,然后测量循环的总耗用时间。在我的i7-8700k计算机上运行循环5次平均需要0.01345秒。使用.iterrows()实现循环我们可以立即做的最简单但非常有价值的加速是使用Pandas的内置.iterrows()函数。在上一节编写for循环时,我们使用了range()函数。然而,当我们在Python中循环大范围的值时,生成器往往会快得多。在这篇文章(https://towardsdatascience.com/5-advancedfeaturesof-python-and-how-use-them-73bffa373c84)中,您可以阅读有关生成器如何工作的更多信息并快速上手。Pandas中的.iterrows()函数在内部实现了一个生成器函数,该函数将在每次迭代中“生成”一行数据。更准确地说,.iterrows()为DataFrame中的每一行生成(index,Series)对(元组)。这实际上与在原始Python中使用类似enumerate()的方法相同,但运行速度更快。下面我们修改代码以使用.iterrows()而不是常规的for循环。在我在上一节中测试的同一台机器上,平均运行时间为0.005892秒——提高了2.28倍!使用.apply()完全抛弃了循环。iterrows()函数极大地加快了速度,但还远远不够。始终记住,在使用专为向量操作设计的库时,可能有一种方法可以非常有效地完成任务,而无需for循环。提供此功能的Pandas函数是.apply()函数。我们的函数.apply()将另一个函数作为其输入并将其沿DataFrame的轴(行、列等)应用。在这种传递函数的情况下,lambda通常可以很方便地将所有内容打包在一起。在下面的代码中,我们用.apply()和lambda函数完全替换了for循环来封装我们想要的计算。在我的机器上,这段代码平均需要0.0020897秒才能运行——比原来的for循环快6.44倍。.apply()更快,因为它在内部尝试迭代Cython迭代器。如果你的函数恰好针对Cython进行了很好的优化,.apply()会让你更快。作为一个额外的好处,使用内置代码会产生更清晰、更易读的代码。最后是用cut。我之前提到过,如果您正在使用专为矢量化操作设计的库,您应该始终寻找一种无需使用for循环即可进行任何计算的方法。同样,以这种方式设计的许多库(包括Pandas)都具有方便的内置函数,可以执行您正在寻找的精确计算——但速度要快得多。Pandas的.cut()函数将一组定义每个If-Else范围的bin和一组定义每个范围返回哪个值的标签作为输入。然后它执行我们用compute_class()函数手动编写的完全相同的操作。查看下面的代码以了解.cut()的工作原理。我们再次获得更清晰、更易读的代码。最后,.cut()函数平均运行了0.001423秒——比原来的for循环快9.39倍!
