如何在Pandas中迭代DataFrame的行
答案:不要这样做*!
Pandas中的迭代是一种反模式,只有在耗尽其他选项时才应该使用。您不应该对带有“iter
”名称的任何函数使用超过几千行,否则您将不得不习惯于等待很长时间。
您想要打印DataFrame吗?请使用DataFrame.to_string()
。
您想要计算某些东西吗?在这种情况下,请按以下顺序搜索方法(列表修改自此处):
- 向量化
- Cython例程
- 列表推导式(传统的
for
循环)
DataFrame.apply()
:i) 可在Cython中执行的归约操作,ii) 在Python空间中进行迭代。
items()
iteritems()
(自v1.5.0起弃用)
DataFrame.itertuples()
DataFrame.iterrows()
iterrows
和itertuples
(在回答此问题时都获得了很多票)应该用于非常罕见的情况,例如为连续处理生成行对象/命名元组,这实际上是这些函数唯一有用的事情。
权威性呼吁
迭代页面上有一个巨大的红色警告框,其中写道:
遍历 pandas 对象通常很慢。在许多情况下,不需要手动遍历行 [...].
*实际情况比“不要”的复杂一些。df.iterrows()
是这个问题的正确答案,但“向量化您的操作”才是更好的答案。我会承认有些情况下无法避免迭代(例如某些操作结果依赖于计算上一行的值)。但是,需要熟悉该库才能知道何时使用。如果您不确定是否需要迭代解决方案,则可能不需要。PS:要了解我编写此答案的基本原理,请跳到最底部。
许多基本操作和计算都被pandas“向量化”(通过NumPy或Cython化的函数)。这包括算术运算、比较、(大多数)缩减、重塑(如透视)、连接和分组操作。请查看基本功能文档,找到适合您问题的向量化方法。
如果没有适合的方法,可以使用自定义Cython扩展编写自己的方法。
如果1)没有可用的向量化解决方案,2)性能很重要,但不重要到需要将代码进行Cython优化的程度,以及3)您正在尝试对代码执行逐元素转换,则列表推导式应该是您的下一个选择。有足够的证据表明,对于许多常见的Pandas任务,列表推导式足够快(甚至有时更快)。
公式很简单,
result = [f(x) for x in df['col']]
result = [f(x, y) for x, y in zip(df['col1'], df['col2'])]
result = [f(row[0], ..., row[n]) for row in df[['col1', ...,'coln']].to_numpy()]
result = [f(row[0], ..., row[n]) for row in zip(df['col1'], ..., df['coln'])]
如果您可以将业务逻辑封装到函数中,则可以使用调用它的列表理解。通过原始Python代码的简单性和速度,您可以使任意复杂的事情都能够运作。
注意事项:
列表理解假设您的数据易于处理 - 这意味着您的数据类型是一致的,并且您没有NaN,但这并不总是可以保证的。
1.第一个比较明显,但处理NaN时,请优先使用内置pandas方法(因为它们具有更好的角落情况处理逻辑),或者确保您的业务逻辑包括适当的NaN处理逻辑。
2.当处理混合数据类型时,应迭代zip(df ['A'],df ['B'],...)而不是df [['A','B']] .to_numpy() ,因为后者会隐式地将数据向上转换为最常见的类型。例如,如果A是数字,而B是字符串,则to_numpy()将将整个数组转换为字符串,这可能不是您想要的。幸运的是,将列一起zip起来是最简单的解决方法。
*由于上面“注意”部分中概述的原因,您的结果可能会有所不同。
一个明显的例子
让我们通过一个简单的例子来演示添加两个pandas列A + B
的差异。这是一种可向量化的操作,因此很容易对比上述方法的性能。
参考代码基准测试。底部的行测量了一个使用numpandas编写的函数,这是一种将Pandas与NumPy混合使用以获得最大性能的风格。除非您知道自己在做什么,否则应避免编写numpandas代码。尽可能使用API(即优先使用vec
而不是vec_numpy
)。
然而,我必须提到的是,并不总是如此明确。有时,“哪种操作方法最佳”这个问题的答案是“取决于您的数据”。我的建议是,在选择一种方法之前,请在您的数据上尝试不同的方法。
我的个人观点*
对于iter家族的各种替代方案进行的大多数分析都是从性能的角度来看的。然而,在大多数情况下,您通常会处理一个相当大小的数据集(不超过几千或100K行),性能将排在解决方案的简单性/可读性之后。
以下是我在选择解决问题的方法时的个人偏好。
对于新手:
向量化(如果可能);apply()
;列表推导式;itertuples()
/iteritems()
;iterrows()
;Cython
对于更有经验的人:
向量化(如果可能);apply()
;列表推导式;Cython;itertuples()
/iteritems()
;iterrows()
向量化是任何可以向量化的问题最惯用的方法。始终寻求向量化!如果有疑问,请查阅文档,或在Stack Overflow上查找与您特定任务相关的现有问题。
我在很多帖子中经常抱怨apply
有多糟糕,但我承认对于初学者来说更容易理解它正在做什么。此外,在我的这篇文章中解释了相当多的apply
用例。
Cython排名较低,因为正确地处理它需要更多的时间和精力。您通常永远不需要编写需要比列表理解更高性能的Pandas代码。
* 如同任何个人意见一样,请充分考虑!
进一步阅读
* Pandas字符串方法是“向量化”的,因为它们在系列上指定但对每个元素进行操作。底层机制仍然是迭代的,因为字符串操作本质上很难向量化。
为什么我写了这个答案
我注意到新用户的一个常见趋势是询问以下形式的问题:“如何迭代我的df以执行X?”。展示调用iterrows()
的代码,同时在for
循环内部执行某些操作。这就是原因。对于尚未介绍向量化概念的库的新用户,可能会将解决其问题的代码想象为迭代其数据以执行某些操作。不知道如何迭代DataFrame,他们所做的第一件事就是谷歌并最终在这里,即此问题。然后,他们看到被接受的答案告诉他们如何做,他们闭上眼睛并运行此代码,而没有首先质疑是否迭代是正确的做法。
此答案旨在帮助新用户了解迭代不一定是每个问题的解决方案,并且更好、更快和更成语化的解决方案可能存在,并且值得投资时间进行探索。我不想发起迭代与向量化之争,但我希望新用户在开发使用此库解决其问题的解决方案时能够得到信息。
最后……一个TLDR来总结这篇文章。
![enter image description here](https://istack.dev59.com/FYfgi.webp)
pandas
也是读取csv文件的首选。使用API来操作数据更加易于编程。 - F.S.