如何在 Pandas 中循环访问数据帧中的行答:不要*!

2022-09-05 00:44:00

我有一个大熊猫数据帧, :df

   c1   c2
0  10  100
1  11  110
2  12  120

如何循环访问此数据帧的行?对于每一行,我希望能够通过列的名称访问其元素(单元格中的值)。例如:

for row in df.rows:
   print(row['c1'], row['c2'])

我发现了一个类似的问题,建议使用其中任何一个:

for date, row in df.T.iteritems():
for row in df.iterrows():

但是我不明白这个物体是什么,以及我如何使用它。row


答案 1

DataFrame.iterrows是一个生成器,它同时生成索引和行(作为序列):

import pandas as pd

df = pd.DataFrame({'c1': [10, 11, 12], 'c2': [100, 110, 120]})
df = df.reset_index()  # make sure indexes pair with number of rows

for index, row in df.iterrows():
    print(row['c1'], row['c2'])
10 100
11 110
12 120

答案 2

如何在熊猫中迭代数据帧中的行?

答:不要*

Pandas中的迭代是一种反模式,只有在用尽所有其他选项时,您才应该这样做。您不应该使用任何名称中带有“”的函数来超过几千行,否则您将不得不习惯于大量的等待。iter

是否要打印数据帧?使用 DataFrame.to_string())。

你想计算一些东西吗?在这种情况下,请按以下顺序搜索方法(从此处修改的列表):

  1. 矢 量化
  2. 赛松套路
  3. 列表理解(香草循环)for
  4. DataFrame.apply(): i) 可以在 Cython 中执行的缩减,ii) Python 空间中的迭代
  5. DataFrame.itertuples() and iteritems()
  6. DataFrame.iterrows()

iterrows和(两者都在回答这个问题时收到许多投票)应该在非常罕见的情况下使用,例如生成行对象/名称进行顺序处理,这实际上是这些函数唯一有用的东西。itertuples

向权威提出上诉

迭代的文档页面有一个巨大的红色警告框,上面写着:

迭代 pandas 对象通常很慢。在许多情况下,不需要手动迭代行 [...]

*它实际上比“不要”复杂一些。df.iterrows() 是这个问题的正确答案,但“矢量化你的操作”是更好的答案。我承认,在某些情况下无法避免迭代(例如,某些操作的结果取决于为上一行计算的值)。但是,需要对库有一定的熟悉才能知道何时。如果您不确定是否需要迭代解决方案,则可能不需要。PS:要了解有关我写这个答案的理由的更多信息,请跳到最底部。


比循环更快:矢量化Cython

大量的基本操作和计算被 pandas“矢量化”(通过 NumPy 或通过 Cythonized 函数)。这包括算术、比较、(大多数)缩减、重塑(如透视)、联接和分组运算。查看有关基本功能的文档,为您的问题找到合适的矢量化方法。

如果不存在,请随时使用自定义Cython扩展编写自己的扩展。


下一个最好的事情:列表理解*

如果 1) 没有可用的矢量化解决方案,2) 性能很重要,但不够重要,无法经历代码规范化的麻烦,以及 3) 您尝试对代码执行元素转换,那么列表推导应该是您的下一个停靠港。有大量证据表明,对于许多常见的Pandas任务,列表理解足够快(有时甚至更快)。

公式很简单,

# Iterating over one column - `f` is some function that processes your data
result = [f(x) for x in df['col']]
# Iterating over two columns, use `zip`
result = [f(x, y) for x, y in zip(df['col1'], df['col2'])]
# Iterating over multiple columns - same data type
result = [f(row[0], ..., row[n]) for row in df[['col1', ...,'coln']].to_numpy()]
# Iterating over multiple columns - differing data type
result = [f(row[0], ..., row[n]) for row in zip(df['col1'], ..., df['coln'])]

如果可以将业务逻辑封装到函数中,则可以使用调用它的列表理解。您可以通过原始Python代码的简单性和速度使任意复杂的事情工作。

警告

列表推导式假设您的数据易于使用 - 这意味着您的数据类型是一致的,并且您没有NaN,但这并不总是可以保证的。

  1. 第一个更明显,但是在处理NaNs时,如果存在内置的pandas方法(因为它们具有更好的角案例处理逻辑),或者确保您的业务逻辑包含适当的NaN处理逻辑。
  2. 在处理混合数据类型时,应循环访问,而不是后者将数据隐式上调为最常用的类型。例如,如果 A 是数字,B 是字符串,则会将整个数组转换为字符串,这可能不是您想要的。幸运的是,将列 ping 在一起是最直接的解决方法。zip(df['A'], df['B'], ...)df[['A', 'B']].to_numpy()to_numpy()zip

*您的里程可能因上述“注意事项”部分所述的原因而有所不同。


一个明显的例子

让我们通过添加两个熊猫列的简单示例来演示差异。这是一个可矢量化的操作,因此很容易对比上面讨论的方法的性能。A + B

基准测试代码,供您参考。底部的行测量用numpandas编写的函数,numpandas是一种Pandas风格,与NumPy大量混合以挤出最佳性能。应该避免编写numpandas代码,除非你知道你在做什么。尽可能坚持使用API(即,更喜欢)。vecvec_numpy

然而,我应该提到,它并不总是这种切割和干燥。有时,“什么是操作的最佳方法”的答案是“这取决于您的数据”。我的建议是在确定数据之前,先对数据测试不同的方法。


我的个人观点*

对迭代器系列的各种替代方案进行的大多数分析都是通过性能的镜头进行的。但是,在大多数情况下,您通常会处理一个大小合理的数据集(不超过几千或100K行),并且性能将仅次于解决方案的简单性/可读性。

这是我在选择用于解决问题的方法时的个人偏好。

对于新手:

矢量化(如果可能);应用();列表理解;itertuples()/iteritems();iterrows();赛松

对于更有经验的人:

矢量化(如果可能);应用();列表理解;赛松;itertuples()/iteritems();迭代()

矢量化是任何可以矢量化的问题的最惯用方法。始终寻求矢量化!如有疑问,请查阅文档,或在Stack Overflow上查找有关您的特定任务的现有问题。

我确实倾向于在我的很多帖子中继续谈论有多糟糕,但我确实承认,对于初学者来说,更容易理解它正在做的事情。此外,在我的这篇文章中已经解释了相当多的用例。applyapply

Cython在列表中排名较低,因为它需要更多的时间和精力才能正确完成。您通常永远不需要使用 pandas 编写代码,这些代码需要这种性能级别,即使是列表理解也无法满足。

*与任何个人意见一样,请与一堆盐一起服用!


延伸阅读

Pandas字符串方法是“矢量化的”,因为它们在序列上指定,但对每个元素进行操作。底层机制仍然是迭代的,因为字符串操作本质上很难矢量化。


为什么我写了这个答案

我从新用户那里注意到的一个常见趋势是询问“我如何迭代我的df来执行X?”的形式。显示在循环内执行某些操作时调用的代码。原因如下。尚未了解矢量化概念的库新用户可能会将解决其问题的代码设想为迭代其数据以执行某些操作。不知道如何迭代DataFrame,他们做的第一件事就是谷歌它,并最终到达这里,在这个问题上。然后,他们看到接受的答案告诉他们如何做,他们闭上眼睛运行这段代码,而没有首先质疑迭代是否正确。iterrows()for

这个答案的目的是帮助新用户理解迭代不一定是每个问题的解决方案,并且可能存在更好,更快,更惯用的解决方案,并且值得花时间探索它们。我并不是想开始一场迭代与矢量化的战争,但我希望新用户在开发解决他们使用这个库的问题的解决方案时得到了解。