如何在 Pandas 中遍历 DataFrame 中的行

问:

我有一个熊猫数据框,df:

   c1   c2
0  10  100
1  11  110
2  12  120

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

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

我发现了一个similar question,它建议使用以下任何一种:

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

for row in df.iterrows():

但我不明白 row 对象是什么以及如何使用它。

答1:

打造属于自己的副业,开启自由职业之旅,从huntsbot.com开始!

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

注意:“因为 iterrows 为每一行返回一个系列,所以它不跨行保留 dtypes。”此外,“你不应该修改你正在迭代的东西。”根据pandas 0.19.1 docs

@viddik13 非常感谢。因此,我遇到了一种情况,其中像 431341610650 这样的数值读作 4.31E+11。有没有办法保留数据类型?

@AzizAlto 使用 itertuples,如下所述。另请参阅pandas.pydata.org/pandas-docs/stable/generated/…

不要使用 iterrows。 Itertuples 更快并保留数据类型。 More info

来自 the documentation:“遍历 pandas 对象通常很慢。在许多情况下,不需要手动遍历行[...]”。您的答案是正确的(在问题的上下文中),但在任何地方都没有提到这一点,所以它不是一个很好的答案。

答2:

huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感

如何在 Pandas 中遍历 DataFrame 中的行?

答案:不要*!

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

你想打印一个DataFrame吗?使用 DataFrame.to_string()。

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

向量化 Cython 例程列表理解(vanilla for loop)DataFrame.apply():i)可以在 Cython 中执行的缩减,ii)Python 空间中的迭代 DataFrame.itertuples() 和 iteritems() DataFrame.iterrows()

iterrows 和 itertuples(在这个问题的答案中都获得了很多投票)应该在非常罕见的情况下使用,例如为顺序处理生成行对象/名称元组,这实际上是这些函数唯一有用的事情。

向当局上诉

The documentation page 在迭代中有一个巨大的红色警告框,上面写着:

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

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

比循环更快:矢量化,Cython

大量的基本操作和计算由 pandas“矢量化”(通过 NumPy 或通过 Cythonized 函数)。这包括算术、比较、(大多数)归约、重塑(例如旋转)、连接和 groupby 操作。查看 Essential Basic Functionality 上的文档,为您的问题找到合适的矢量化方法。

如果不存在,请随意使用自定义 Cython extensions 编写您自己的。

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

如果 1) 没有可用的矢量化解决方案,列表推导应该是您的下一个停靠点,2) 性能很重要,但还不足以解决对代码进行 cythonizing 的麻烦,以及 3) 您正在尝试执行元素转换在你的代码上。有一个 good amount of evidence 表明列表推导对于许多常见的 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,但这并不总是得到保证。

第一个更明显,但是在处理 NaN 时,如果存在内置的 pandas 方法(因为它们具有更好的极端情况处理逻辑),则更喜欢它们,或者确保您的业务逻辑包含适当的 NaN 处理逻辑。在处理混合数据类型时,您应该迭代 zip(df[‘A’], df[‘B’], …) 而不是 df[[‘A’, ‘B’]].to_numpy() 作为后者隐式地将数据向上转换为最常见的类型。例如,如果 A 是数字而 B 是字符串,则 to_numpy() 会将整个数组转换为字符串,这可能不是您想要的。幸运的是,将列压缩在一起是最直接的解决方法。

*您的里程可能会因上述注意事项部分中列出的原因而有所不同。

一个明显的例子

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

Benchmarking code, for your reference。底部的行测量了一个用 numpandas 编写的函数,这是一种与 NumPy 大量混合以挤出最大性能的 Pandas 风格。除非您知道自己在做什么,否则应避免编写 numpandas 代码。尽可能坚持使用 API(即,更喜欢 vec 而不是 vec_numpy)。

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

我的个人意见 *

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

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

对于新手:

矢量化(如果可能);申请();列出理解; itertuples()/iteritems(); iterrows();赛通

对于更有经验的人:

矢量化(如果可能);申请();列出理解;赛通; itertuples()/iteritems(); iterrows()

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

我确实倾向于在我的很多帖子中继续谈论 apply 的糟糕程度,但我承认初学者更容易理解它在做什么。此外,this post of mine 中解释了 apply 的很多用例。

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

*与任何个人意见一样,请多加盐!

延伸阅读

10 分钟了解 pandas 和基本基本功能 - 向您介绍 Pandas 及其矢量化*/cythonized 函数库的有用链接。

增强性能 - 增强标准 Pandas 操作的文档入门

pandas 中的 for 循环真的很糟糕吗?我什么时候应该关心? - 我对列表理解及其对各种操作的适用性(主要是涉及非数字数据的操作)的详细说明

我什么时候应该(不)想在我的代码中使用 pandas apply()? - apply 很慢(但不像 iter* 系列那么慢。但是,在某些情况下可以(或应该)考虑将 apply 作为一种重要的替代方案,尤其是在某些 GroupBy 操作中)。

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

为什么我写这个答案

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

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

请注意,iterrows 和 itertuples 有一些重要的注意事项。有关详细信息,请参阅 this answer 和 pandas docs。

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

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

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

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

答3:

huntsbot.com精选全球7大洲远程工作机会,涵盖各领域,帮助想要远程工作的数字游民们能更精准、更高效的找到对方。

首先考虑您是否真的需要迭代 DataFrame 中的行。请参阅 this answer 了解替代方案。

如果您仍然需要遍历行,可以使用下面的方法。请注意一些其他答案中未提及的重要警告。

DataFrame.iterrows() 用于索引,df.iterrows() 中的行: print(row[“c1”], row[“c2”])

DataFrame.itertuples() for row in df.itertuples(index=True, name=‘Pandas’): print(row.c1, row.c2)

itertuples() 应该比 iterrows() 快

但请注意,根据文档(目前为 pandas 0.24.2):

iterrows:dtype 可能与行不匹配

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

iterrows:不修改行

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

请改用 DataFrame.apply():

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

迭代:

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

有关详细信息,请参阅 pandas docs on iteration。

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

注意:您也可以说 for row in df[['c1','c2']].itertuples(index=True, name=None): 之类的内容来仅在行迭代器中包含某些列。

您可以只使用 row.c1 而不是 getattr(row, "c1")。

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

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

答4:

与HuntsBot一起,探索全球自由职业机会–huntsbot.com

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

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

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

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

答5:

与HuntsBot一起,探索全球自由职业机会–huntsbot.com

虽然 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

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

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

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

@AbeMiessler iterrows() 将每行数据打包成一个系列,而 itertuples() 没有。

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

答6:

huntsbot.com全球7大洲远程工作机会,探索不一样的工作方式

您可以按如下方式使用 df.iloc 函数:

for i in range(0, len(df)):
    print(df.iloc[i]['c1'], df.iloc[i]['c2'])

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

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

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

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

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

答7:

huntsbot.com – 高效赚钱,自由工作

您还可以使用 df.apply() 迭代行并访问函数的多个列。

docs: DataFrame.apply()

def valuation_formula(x, y):
    return x * y * 0.5

df['price'] = df.apply(lambda row: valuation_formula(row['x'], row['y']), axis=1)

df['price'] 是指数据框中的列名吗?我正在尝试创建一个字典,其中包含 csv 文件中多个列的唯一值。我用你的逻辑创建了一个具有唯一键和值的字典,并得到一个错误说明 TypeError: ("'Series' objects are mutable, 因此它们不能被散列", u'occured at index 0')

代码:df['Workclass'] = df.apply(lambda row: dic_update(row), axis=1) end of line id = 0 end of line def dic_update(row): if row not in dic: dic[row] = ID ID = ID + 1

将轴默认为 0 是最糟糕的

请注意,apply 不会“迭代”行,而是逐行应用函数。如果您确实确实 需要迭代和indeces,例如在比较不同行的值时(在这种情况下,您只能进行迭代),则上述代码将不起作用。

这是熊猫的合适答案

答8:

huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感

如何高效迭代

如果您确实需要迭代 Pandas 数据框,您可能希望避免使用 iterrows()。有不同的方法,通常的 iterrows() 远不是最好的。 itertuples() 可以快 100 倍。

简而言之:

作为一般规则,使用 df.itertuples(name=None)。特别是当您有固定数量的列且少于 255 列时。见第 (3) 点

否则,请使用 df.itertuples(),除非您的列有特殊字符,例如空格或“-”。见点(2)

通过使用最后一个示例,即使您的数据框有奇怪的列,也可以使用 itertuples()。见第 (4) 点

如果您不能使用以前的解决方案,请仅使用 iterrows()。见点(1)

遍历 Pandas 数据框中的行的不同方法:

生成具有一百万行和 4 列的随机数据帧:

    df = pd.DataFrame(np.random.randint(0, 100, size=(1000000, 4)), columns=list('ABCD'))
    print(df)

  1. 通常的 iterrows() 很方便,但该死的慢:
start_time = time.clock()
result = 0
for _, row in df.iterrows():
    result += max(row['B'], row['C'])

total_elapsed_time = round(time.clock() - start_time, 2)
print("1. Iterrows done in {} seconds, result = {}".format(total_elapsed_time, result))

  1. 默认的 itertuples() 已经快得多了,但它不适用于 My Col-Name is very Strange 之类的列名(如果您的列重复或列名不能简单地转换为 Python 变量,则应避免使用此方法姓名)。:
start_time = time.clock()
result = 0
for row in df.itertuples(index=False):
    result += max(row.B, row.C)

total_elapsed_time = round(time.clock() - start_time, 2)
print("2. Named Itertuples done in {} seconds, result = {}".format(total_elapsed_time, result))

  1. 使用 name=None 的默认 itertuples() 更快,但不是很方便,因为您必须为每列定义一个变量。
start_time = time.clock()
result = 0
for(_, col1, col2, col3, col4) in df.itertuples(name=None):
    result += max(col2, col3)

total_elapsed_time = round(time.clock() - start_time, 2)
print("3. Itertuples done in {} seconds, result = {}".format(total_elapsed_time, result))

  1. 最后,命名的 itertuples() 比前一点慢,但您不必为每列定义一个变量,它适用于列名,例如 My Col-Name is very Strange。
start_time = time.clock()
result = 0
for row in df.itertuples(index=False):
    result += max(row[df.columns.get_loc('B')], row[df.columns.get_loc('C')])

total_elapsed_time = round(time.clock() - start_time, 2)
print("4. Polyvalent Itertuples working even with special characters in the column name done in {} seconds, result = {}".format(total_elapsed_time, result))

输出:

         A   B   C   D
0       41  63  42  23
1       54   9  24  65
2       15  34  10   9
3       39  94  82  97
4        4  88  79  54
...     ..  ..  ..  ..
999995  48  27   4  25
999996  16  51  34  28
999997   1  39  61  14
999998  66  51  27  70
999999  51  53  47  99

[1000000 rows x 4 columns]

1. Iterrows done in 104.96 seconds, result = 66151519
2. Named Itertuples done in 1.26 seconds, result = 66151519
3. Itertuples done in 0.94 seconds, result = 66151519
4. Polyvalent Itertuples working even with special characters in the column name done in 2.94 seconds, result = 66151519

This article is a very interesting comparison between iterrows and itertuples

那么为什么这些低效的方法首先在 Pandas 中可用 - 如果“常识”不应该使用 iterrows 和 itertuples - 那么为什么它们在那里,或者更确切地说,为什么这些方法没有更新并在熊猫维护者的背景?

@Monty,并不总是可以矢量化所有操作。

答9:

一个优秀的自由职业者,应该有对需求敏感和精准需求捕获的能力,而huntsbot.com提供了这个机会

我一直在寻找如何迭代行和列并在这里结束:

for i, row in df.iterrows():
    for j, column in row.iteritems():
        print(column)

如果可能,您应该避免使用 iterrows()。我在答案 How to iterate efficiently 中解释了原因

答10:

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

您可以编写自己的迭代器来实现 namedtuple

from collections import namedtuple

def myiter(d, cols=None):
    if cols is None:
        v = d.values.tolist()
        cols = d.columns.values.tolist()
    else:
        j = [d.columns.get_loc(c) for c in cols]
        v = d.values[:, j].tolist()

    n = namedtuple('MyTuple', cols)

    for line in iter(v):
        yield n(*line)

这与 pd.DataFrame.itertuples 直接可比较。我的目标是更高效地执行相同的任务。

对于具有我的功能的给定数据框:

list(myiter(df))

[MyTuple(c1=10, c2=100), MyTuple(c1=11, c2=110), MyTuple(c1=12, c2=120)]

或使用 pd.DataFrame.itertuples:

list(df.itertuples(index=False))

[Pandas(c1=10, c2=100), Pandas(c1=11, c2=110), Pandas(c1=12, c2=120)]

全面测试我们测试使所有列可用并对列进行子集化。

def iterfullA(d):
    return list(myiter(d))

def iterfullB(d):
    return list(d.itertuples(index=False))

def itersubA(d):
    return list(myiter(d, ['col3', 'col4', 'col5', 'col6', 'col7']))

def itersubB(d):
    return list(d[['col3', 'col4', 'col5', 'col6', 'col7']].itertuples(index=False))

res = pd.DataFrame(
    index=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
    columns='iterfullA iterfullB itersubA itersubB'.split(),
    dtype=float
)

for i in res.index:
    d = pd.DataFrame(np.random.randint(10, size=(i, 10))).add_prefix('col')
    for j in res.columns:
        stmt = '{}(d)'.format(j)
        setp = 'from __main__ import d, {}'.format(j)
        res.at[i, j] = timeit(stmt, setp, number=100)

res.groupby(res.columns.str[4:-1], axis=1).plot(loglog=True);

https://i.stack.imgur.com/rt88e.png

https://i.stack.imgur.com/azbOF.png

对于不想阅读代码的人:蓝线是 intertuples,橙线是通过 yield 块的迭代器列表。 interrows 不进行比较。

答11:

HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com

要循环 dataframe 中的所有行,您可以使用:

for x in range(len(date_example.index)):
    print date_example['Date'].iloc[x]

这是链式索引。我不建议这样做。

@cs95 你会推荐什么?

如果您想完成这项工作,请调用 df.columns.get_loc 以获取日期列的整数索引位置(循环外),然后在内部使用单个 iloc 索引调用。

原文链接:https://www.huntsbot.com/qa/WyLq/how-to-iterate-over-rows-in-a-dataframe-in-pandas?lang=zh_CN&from=csdn

保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com

你可能感兴趣的:(pandas,python,数据分析,ipad,tfs)