问答中心分类: DATABASE如何在 Pandas 中遍历 DataFrame 中的行
0
匿名用户 提问 35分钟 前

我有一个熊猫数据框,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对象是以及我如何使用它。

Stefan Gruenwald 回复 35分钟 前

df.iteritems() 迭代列而不是行。因此,要使其遍历行,您必须转置(“T”),这意味着您将行和列相互更改(反映在对角线上)。因此,当您使用 df.T.iteritems() 时,您可以有效地在其行上迭代原始数据帧

Stefan Gruenwald 回复 35分钟 前

与 cs95 所说的相反,想要迭代数据帧是有充分理由的,因此新用户不应该感到气馁。一个例子是,如果您想使用每一行的值作为输入来执行一些代码。此外,如果您的数据框相当小(例如少于 1000 个项目),则性能并不是真正的问题。

Stefan Gruenwald 回复 35分钟 前

@cs95 在我看来,数据框是 Python 中的首选表格式。因此,无论何时您想要读取 csv,或者您有一个要操作其值的 dicts 列表,或者您想要执行简单的连接、groupby 或窗口操作,您都可以使用数据框,即使您的数据比较小。

Stefan Gruenwald 回复 35分钟 前

@cs95 不,但这是对“完全使用 DataFrame”的回应。我的观点是,这就是为什么人们可能在数据框中拥有自己的数据的原因。然后,如果您想为数据的每一行运行一个脚本,则必须遍历该数据框。

Stefan Gruenwald 回复 35分钟 前

我第二个@oulenz。据我所知pandas即使数据集很小,也是读取 csv 文件的首选。使用 API 操作数据更容易编程

Stefan Gruenwald 回复 35分钟 前

如果你是这个线程的初学者并且不熟悉 pandas 库,那么值得退一步评估一下迭代是否的确您的问题的解决方案。在某些情况下,确实如此。在大多数情况下,它不是。我在下面的帖子通过让初学者了解向量化的概念来向他们介绍该库,以便他们了解编写“好代码”与“正常工作的代码”之间的区别——并​​且还知道何时使用哪个。有些人对后者很满意,他们可以继续对@oulenz 的评论进行尽可能多的投票。

Stefan Gruenwald 回复 35分钟 前

我需要用两个字母 + 人口生成美国各州的列表。有什么比迭代更好的方法。我的 df 并使用“打印”?

Stefan Gruenwald 回复 35分钟 前

利用df.apply.有关详细信息,请参阅geeksforgeeks.org/…

29 Answers
0
cs95 回答 35分钟 前

如何在 Pandas 中遍历 DataFrame 中的行

答案:不要*!
Pandas 中的迭代是一种反模式,只有在用尽所有其他选项时才应该这样做。您不应将任何功能与“iter” 以它的名称显示超过几千行,否则您将不得不习惯很多的等待。
你想打印一个DataFrame吗?利用DataFrame.to_string().
你想计算一些东西吗?在这种情况下,按此顺序搜索方法(列表修改自这里):

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

iterrowsitertuples(在这个问题的答案中都获得了很多投票)应该在非常罕见的情况下使用,例如为顺序处理生成行对象/名称,这实际上是这些函数唯一有用的事情。
向当局上诉
文档页面在迭代中有一个巨大的红色警告框,上面写着:

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

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

比循环更快:矢量化,赛通
大量的基本操作和计算由 pandas“矢量化”(通过 NumPy 或通过 Cythonized 函数)。这包括算术、比较、(大多数)归约、重塑(例如旋转)、连接和 groupby 操作。浏览文档基本的基本功能为您的问题找到合适的矢量化方法。
如果不存在,请随意使用自定义编写自己的Cython 扩展.

下一个最好的事情:列出理解*
如果 1) 没有可用的矢量化解决方案,列表推导应该是您的下一个停靠点,2) 性能很重要,但还不足以解决对代码进行 cythonize 的麻烦,以及 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. 第一个更明显,但是在处理 NaN 时,如果存在内置的 pandas 方法(因为它们具有更好的极端情况处理逻辑),则更喜欢它们,或者确保您的业务逻辑包含适当的 NaN 处理逻辑。
  2. 在处理混合数据类型时,您应该迭代zip(df['A'], df['B'], ...)代替df[['A', 'B']].to_numpy()因为后者隐式地将数据向上转换为最常见的类型。例如,如果 A 是数字,B 是字符串,to_numpy()会将整个数组转换为字符串,这可能不是您想要的。幸运的是zip将您的列ping在一起是最直接的解决方法。

*您的里程可能会因以下列出的原因而有所不同注意事项上一节。

一个明显的例子
让我们通过添加两个 pandas 列的简单示例来演示差异A + B.这是一个可向量化的操作,因此很容易对比上述方法的性能。
回答如何在 Pandas 中遍历 DataFrame 中的行
基准代码,供您参考.底部的行测量了一个用 numpandas 编写的函数,这是一种与 NumPy 大量混合以挤出最大性能的 Pandas 风格。除非您知道自己在做什么,否则应避免编写 numpandas 代码。尽可能坚持使用 API(即,更喜欢vec超过vec_numpy)。
然而,我应该提一下,它并不总是这么干脆利落的。有时,“什么是最佳操作方法”的答案是“这取决于您的数据”。我的建议是在确定一种方法之前对您的数据测试不同的方法。

我的个人意见*
对 iter 系列的各种替代方案进行的大多数分析都是从性能的角度进行的。但是,在大多数情况下,您通常会处理大小合理的数据集(不超过几千或 100K 行),性能将仅次于解决方案的简单性/可读性。
这是我在选择用于解决问题的方法时的个人偏好。
对于新手:

矢量化(若有可能);apply();列出理解;itertuples()/iteritems();iterrows();赛通

对于更有经验的人:

矢量化(若有可能);apply();列出理解;赛通;itertuples()/iteritems();iterrows()

对于可以向量化的任何问题,向量化是最惯用的方法。始终寻求矢量化!如有疑问,请查阅文档,或在 Stack Overflow 上查看有关您的特定任务的现有问题。
我确实倾向于继续谈论有多糟糕apply在我的很多帖子中都有,但我承认初学者更容易理解它正在做什么。此外,还有很多用例apply已经解释过我的这个帖子.
Cython 在列表中排名较低,因为它需要更多的时间和精力才能正确完成。您通常永远不需要使用 pandas 编写需要这种性能水平的代码,即使是列表推导也无法满足。
*与任何个人意见一样,请多加盐!

延伸阅读

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

为什么我写这个答案
我从新用户那里注意到的一个常见趋势是提出“如何迭代我的 df 以执行 X?”形式的问题。显示调用的代码iterrows()在里面做某事的时候for环形。这就是为什么。一个没有被引入向量化概念的库的新用户可能会将解决他们问题的代码设想为迭代他们的数据来做某事。不知道如何迭代 DataFrame,他们做的第一件事就是用谷歌搜索它,然后在这个问题上结束。然后,他们看到接受的答案告诉他们如何去做,然后他们闭上眼睛运行这段代码,而不会首先质疑迭代是否是正确的做法。
这个答案的目的是帮助新用户理解迭代不一定是所有问题的解决方案,并且可能存在更好、更快和更惯用的解决方案,值得花时间去探索它们。我并不是要开始一场迭代与矢量化的战争,但我希望新用户在为他们的这个库的问题开发解决方案时被告知。

viddik13 回复 35分钟 前

请注意,有一些重要的警告iterrowsitertuples.看这个答案熊猫文档更多细节。

LinkBerest 回复 35分钟 前

这是唯一一个专注于应该与 pandas 一起使用的惯用技术的答案,使其成为该问题的最佳答案。学习获取正确的 回答 正确的 代码(而不是正确的 回答 错误的 代码– 即低效、无法扩展、太适合特定数据)是学习熊猫(以及一般数据)的重要组成部分。

Imperishable Night 回复 35分钟 前

不过,我认为您对 for 循环不公平,因为在我的测试中它们只比列表理解慢一点。诀窍是循环zip(df['A'], df['B'])代替df.iterrows().

sdbbs 回复 35分钟 前

好的,我明白你的意思,但是如果我需要打印表格的每一行(带有数字数据),按升序排序 – 我想除了循环遍历这些行之外别无他法,对吧?

cs95 回复 35分钟 前

@sdbbs,使用 sort_values 对数据进行排序,然后对结果调用 to_string() 。

David Wasserman 回复 35分钟 前

在 List Comprehensions 下,“迭代多列”示例需要注意:DataFrame.values将每一列转换为通用数据类型。DataFrame.to_numpy()也这样做。幸运的是我们可以使用zip具有任意数量的列。

cs95 回复 35分钟 前

@DavidWasserman 这是一个很棒的评论,感谢您的评论。实际上,除非您先转换为对象(这,您为什么要转换),否则混合列需要注意这一点!

c z 回复 35分钟 前

有趣,因为iterrows,apply和列表理解似乎都倾向于上)可扩展性我会避免任何微优化并选择最易读的。使用任何方法都太慢的数据集更有可能需要花费时间寻找不适合的解决方案Pandas,而不是试图减少毫秒for-环形。

cs95 回复 35分钟 前

@cz 该图是对数的。较大数据集的性能差异以秒和分钟为单位,而不是毫秒。

bug_spray 回复 35分钟 前

我知道我迟到了,但是如果你将数据帧转换为 numpy 数组,然后使用矢量化,它甚至比 pandas 数据帧矢量化还要快(这包括将其转换回数据帧系列的时间)。例如:def np_vectorization(df): np_arr = df.to_numpy() return pd.Series(np_arr[:,0] + np_arr[:,1], index=df.index)和…def just_np_vectorization(df): np_arr = df.to_numpy() return np_arr[:,0] + np_arr[:,1]

cs95 回复 35分钟 前

@AndreRicardo 为什么不将其发布在它变得更加明显的答案中?

Mike_K 回复 35分钟 前

这实际上是我很难找到答案中描述的谷歌路径。谢谢你!

Aleksandr Panzin 回复 35分钟 前

不幸的是,我们中的一些人没有选择遵循您的建议。因为有些库只是强制使用 DataFrame,这是不必要的。 (我来到这里试图在没有 Spark 的情况下在 Python 中迭代 parquet 文件并将数据转换为 JSON。我被迫使用 DataFrame)如果您编写库 – 请记住不要将 Pandas 推向我们。

cs95 回复 35分钟 前

@Dean 我经常收到这个回复,老实说让我很困惑。这一切都是为了养成良好的习惯。 “我的数据很小,性能并不重要,所以我可以原谅我使用这种反模式”..?当有一天性能确实很重要时,您会感谢自己提前准备了正确的工具。

sh37211 回复 35分钟 前

“公式很简单,…”后面的代码示例都是关于迭代列或一组硬编码的列。我们中的一些人希望对行中的所有数据进行每行一次操作,即使用所有数字和文本(例如文件名),而不必对每个列名进行硬编码。编辑代码示例以使其更清楚如何做整行会改善这个答案。

cs95 回复 35分钟 前

@sh37211“迭代多种类型”的情况可以扩大到满足该要求:result = [f(row[0], ..., row[n]) for row in df.to_numpy()]应该可以正常工作,不需要硬编码。这不是你想要的吗?

Ram 回复 35分钟 前

谢谢你的详细回答。这真的很有帮助。

Wes Turner 回复 35分钟 前

在从这些几乎实际的矢量化方法中进行选择时,是否有机会改进例如 dask with pandas? (FWIW,对于分散式大熊猫黎明, 存在cudf.DataFrame.applymap()dask.dataframe.DataFrame.map_partitions() docs.rapids.ai/api/cudf/nightly/user_guide/10min.html#Applymap)

katamayros 回复 35分钟 前

这是一个很好的解释。然而,有些人可能需要使用数据帧中的数据(例如 URL)来下载文件。在这种情况下, iterrows() 是前进的方向。

Geoff Langenderfer 回复 35分钟 前

我讨厌这种愚蠢的做法。如果您的数据足够大,只需使用 scala。使用所有你想要的 for 循环。

cs95 回复 35分钟 前

@GeoffLangenderfer“改变语言”似乎用手提钻杀死蚊子。想象一下强迫自己学习一门新语言,因为你需要满足你对编写 for 循环的贪得无厌的渴望:D

jjepsuomi 回复 35分钟 前

嗨@cs95 在我的情况下会推荐什么:我有一堆地理坐标(从几千到几百万不等),我需要遍历它们。对于每个坐标,我需要在坐标点周围打开 X 数量的 GIS 光栅子图像,使用 N 个自定义 3rd 方函数(例如 Gabor 滤波器等)对这些子图像进行处理,然后保存这些结果。我假设我的过程不可矢量化,所以我唯一的选择是使用iterrows这里?或者,我需要制作一个高度自定义的代码才能通过矢量化来做到这一点?

stucash 回复 35分钟 前

如果第三列为真,我正在尝试为每一行打印前两列中的值的语句;除了迭代器/for循环之外,我似乎什么都看不到;在 1.4.2iterrows仍然留在这里,我猜图书馆确实认为他们有有效的场景?

BeRT2me 回复 35分钟 前

@cs95 在哪里实现/使用可以利用的功能applyraw=True功能属于这个层次结构?

Banik 回复 35分钟 前

虽然这是一个很好的答案,它试图向初学者展示良好的习惯,他们在进入大数据应用程序时可能会以错误的方式感到惊讶,但我很惊讶没有人提到另一个方面:代码的可读性.这在软件工程中是一个非常重要的问题,尤其是在团队协作时。显然,性能确实很重要,但是,如果数据真的无法扩展,那么编写不可读的代码对任何人都没有帮助,因为其他应用程序在更大数据上的“习惯”。

brec 回复 35分钟 前

其中:有两个数据框有一个共同的“键”列——两个列标题相同——包含字符串类型的唯一键值;具有较大索引(更多行)的数据帧应该包含另一个(较小索引)数据帧中的所有键值;有没有一种非迭代的方法来检查较小的每个键值是否实际上包含在较大的值中?

0
viddik13 回答 35分钟 前

首先考虑是否真的需要迭代在 DataFrame 中的行上。看这个答案为替代品。
如果您仍然需要遍历行,可以使用下面的方法。注意一些重要警告在任何其他答案中都没有提到。

itertuples()应该比iterrows()
但请注意,根据文档(目前为 pandas 0.24.2):

  • 迭代:dtype可能不会逐行匹配

因为 iterrows 为每一行返回一个系列,所以它不保存跨行的数据类型(数据帧跨列保留数据类型)。要在遍历行时保留 dtypes,最好使用 itertuples(),它返回值的命名元组,通常比 iterrows() 快得多

  • iterrows:不修改行

你应该从不修改你正在迭代的东西。这不能保证在所有情况下都有效。根据数据类型,迭代器返回一个副本而不是一个视图,写入它不会有任何效果。

利用DataFrame.apply()反而:

new_df = df.apply(lambda x: x * 2, axis = 1)
  • 迭代:

如果列名是无效的 Python 标识符、重复或以下划线开头,它们将被重命名为位置名称。对于大量列 (>255),将返回常规元组。

迭代中的熊猫文档更多细节。

Raul Guarini Riva 回复 35分钟 前

只是有人在完成后很长时间阅读该线程的一个小问题: df.apply() 在效率方面与 itertuples 相比如何?

Brian Burns 回复 35分钟 前

注意:你也可以这样说for row in df[['c1','c2']].itertuples(index=True, name=None):仅在行迭代器中包含某些列。

viraptor 回复 35分钟 前

代替getattr(row, "c1"), 你可以只使用row.c1.

Noctiphobia 回复 35分钟 前

我有 90% 的把握,如果你使用getattr(row, "c1")代替row.c1,您将失去任何性能优势itertuples,如果您确实需要通过字符串访问该属性,则应使用 iterrows 代替。

Marlo 回复 35分钟 前

当我尝试这个时,它只打印列值而不是标题。列标题是否从行属性中排除?

viddik13 回复 35分钟 前

我偶然发现了这个问题,因为虽然我知道有拆分应用组合,但我仍然真的需要迭代在 DataFrame 上(如问题所述)。不是每个人都有奢侈品可以改进numbacython(相同的文档说“首先在 Python 中优化总是值得的”)。我写这个答案是为了帮助其他人避免(有时令人沮丧)的问题,因为其他答案都没有提到这些警告。误导任何人或告诉“这是正确的做法”从来都不是我的本意。我已经改进了答案。

Confounded 回复 35分钟 前

如果我想遍历步长大于 1 的数据帧,例如只选择每第三行怎么办?谢谢

viddik13 回复 35分钟 前

@Confounded 一个快速的谷歌显示你可以使用 iloc 来预处理数据框:df.iloc[::5, :]会给你每个第 5 行。看这个问题更多细节。

David Doria 回复 35分钟 前

仅供参考,“迭代中的熊猫文档”链接已损坏。

Muhammad Yasirroni 回复 35分钟 前

我不知道为什么,但使用name=None制作itertuples在我的用例中快 50%。

0
Wes McKinney 回答 35分钟 前

你应该使用df.iterrows().尽管逐行迭代并不是特别有效,因为Series必须创建对象。

vgoklani 回复 35分钟 前

这是否比将 DataFrame 转换为 numpy 数组(通过 .values)并直接对数组进行操作更快?我有同样的问题,但最终转换为 numpy 数组,然后使用 cython。

Phillip Cloud 回复 35分钟 前

@vgoklani如果逐行迭代效率低下并且您有一个非对象numpy数组,那么几乎可以肯定使用原始numpy数组会更快,特别是对于具有多行的数组。你应该避免迭代行,除非你绝对必须

Richard Wong 回复 35分钟 前

我对 df.iterrows()、df.itertuples() 和 zip(df[‘a’], df[‘b’]) 的时间消耗进行了一些测试,并将结果发布在另一个答案中问题:stackoverflow.com/a/34311080/2142098

0
e9t 回答 35分钟 前

尽管iterrows()是一个不错的选择,有时itertuples()可以更快:

df = pd.DataFrame({'a': randn(1000), 'b': randn(1000),'N': randint(100, 1000, (1000)), 'x': 'x'})

%timeit [row.a * 2 for idx, row in df.iterrows()]
# => 10 loops, best of 3: 50.3 ms per loop

%timeit [row[1] * 2 for row in df.itertuples()]
# => 1000 loops, best of 3: 541 µs per loop
Alex 回复 35分钟 前

您的两个示例中的大部分时间差异似乎是由于您似乎对 .iterrows() 命令使用基于标签的索引,而对 .itertuples() 命令使用基于整数的索引。

harbun 回复 35分钟 前

对于基于金融数据的数据帧(时间戳和 4 倍浮点数),itertuples 比我的机器上的 iterrows 快 19,57 倍。仅有的for a,b,c in izip(df["a"],df["b"],df["c"]:几乎同样快。

Abe Miessler 回复 35分钟 前

你能解释为什么它更快吗?

miradulo 回复 35分钟 前

@AbeMiessleriterrows()将每一行数据放入一个系列中,而itertuples()才不是。

Brian Burns 回复 35分钟 前

请注意,列的顺序实际上是不确定的,因为df是从字典创建的,所以row[1]可以引用任何列。事实证明,尽管整数与浮点列的时间大致相同。

Alex 回复 35分钟 前

@jeffhale 您引用的时间完全相同,这怎么可能?另外,当我提到基于整数的索引时,我的意思是类似于 row.iat[1]。

jeffhale 回复 35分钟 前

@Alex 看起来确实很可疑。我只是重新运行了几次,itertuples 比 iterrows 长了 3 倍。使用熊猫 0.23.4。将删除其他评论以避免混淆。

jeffhale 回复 35分钟 前

然后在更大的 DataFrame 上运行,更像是真实世界的情况,itertuples 比 iterrows 快 100 倍。 Itertuples 为胜利。

Ajasja 回复 35分钟 前

我也得到了> 50倍的增长i.stack.imgur.com/HBe9o.png(在第二次运行时更改为 attr 访问器)。

0
PJay 回答 35分钟 前

您可以使用df.iloc功能如下:

for i in range(0, len(df)):
    print(df.iloc[i]['c1'], df.iloc[i]['c2'])
rocarvaj 回复 34分钟 前

我知道应该避免这种情况,而支持 iterrows 或 itertuples,但知道为什么会很有趣。有什么想法吗?

Ken Williams 回复 34分钟 前

如果您想保留数据类型并按名称引用列,这是我所知道的唯一有效技术。itertuples保留数据类型,但去掉它不喜欢的任何名称。iterrows相反。

Sean Anderson 回复 34分钟 前

花了几个小时试图通过 pandas 数据结构的特质来做一些简单而富有表现力的事情。这会产生可读的代码。

Kim Miller 回复 34分钟 前

尽管for i in range(df.shape[0])可能会加快这种方法的速度,但对于我的应用程序,它仍然比上面的 iterrows() 方法慢 3.5 倍。

Bastiaan 回复 34分钟 前

在大型 Dataframes 上,这似乎更好my_iter = df.itertuples()需要双倍的内存和大量的时间来复制它。同样的iterrows().