如何迭代Pandas中的DataFrame中的行?

Rom*_*man 1551 python rows dataframe pandas

我有一只DataFrame熊猫:

import pandas as pd
inp = [{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}]
df = pd.DataFrame(inp)
print df
Run Code Online (Sandbox Code Playgroud)

输出:

   c1   c2
0  10  100
1  11  110
2  12  120
Run Code Online (Sandbox Code Playgroud)

现在我想迭代这个帧的行.对于每一行,我希望能够通过列的名称访问其元素(单元格中的值).例如:

for row in df.rows:
   print row['c1'], row['c2']
Run Code Online (Sandbox Code Playgroud)

是否有可能在熊猫中做到这一点?

我发现了类似的问题.但它没有给我我需要的答案.例如,建议使用:

for date, row in df.T.iteritems():
Run Code Online (Sandbox Code Playgroud)

要么

for row in df.iterrows():
Run Code Online (Sandbox Code Playgroud)

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

wai*_*kuo 2141

DataFrame.iterrows是一个生成索引和行的生成器

import pandas as pd
import numpy as np

df = pd.DataFrame([{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}])
Run Code Online (Sandbox Code Playgroud)

  • 注意:"因为iterrows为每一行返回一个Series,所以**不会**保留行中的dtypes." 此外,"你**永远不应该修改你正在迭代的东西." 根据[pandas 0.19.1 docs](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.iterrows.html) (172认同)
  • 不要使用iterrows.Itertuples更快,并保留数据类型.[更多信息](/sf/answers/2871598831/) (77认同)
  • @AzizAlto使用`itertuples`,如下所述.另见http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.itertuples.html (13认同)
  • 摘自[文档](https://pandas.pydata.org/pandas-docs/stable/getting_started/basics.html#iteration):“遍历pandas对象通常很慢。在许多情况下,手动遍历行是没有必要[...]”。您的答案是正确的(在问题的背景下),但是在任何地方都没有提及,因此它不是一个很好的答案。 (8认同)
  • @ viddik13非常感谢.因此,我遇到了像`431341610650`这样的数值被读作`4.31E + 11`的情况.有没有办法保存dtypes? (3认同)
  • 如果你不需要保留数据类型,那么iterrows很好.@ waitingkuo的提示分开索引使解析更容易. (2认同)
  • 根据https://stackoverflow.blog/2021/04/19/how-often-do-people-actually-copy-and-paste-from-stack-overflow-now-we-,这是stackoverflow上复制最多的答案知道/ (2认同)

vid*_*k13 332

要在pandas中迭代DataFrame的行,可以使用:

itertuples() 应该比...更快 iterrows()

但请注意,根据文档(目前的pandas 0.21.1):

  • iterrows:dtype可能在行与行之间不匹配

    因为iterrows为每一行返回一个Series,所以它不会保留行中的dtypes(dtypes保留在DataFrames的列之间).

  • iterrows:不要修改行

    永远不应该修改你正在迭代的东西.这并不能保证在所有情况下都有效.根据数据类型,迭代器返回副本而不是视图,并且写入它将不起作用.

    请改用DataFrame.apply():

    new_df = df.apply(lambda x: x * 2)
    
    Run Code Online (Sandbox Code Playgroud)
  • itertuples:

    如果列名称是无效的Python标识符,重复或以下划线开头,则列名称将重命名为位置名称.使用大量列(> 255)时,将返回常规元组.

  • 而不是`getattr(row,"c1")`,你可以只使用`row.c1`. (9认同)
  • 只是一个小问题,有人在完成这么长时间后阅读这个帖子:df.apply()在效率方面与itertuples相比如何? (3认同)
  • 注意:你也可以说'for row in df [['c1','c2']].itertuples(index = True,name = None):`只包含行迭代器中的某些列. (3认同)
  • 我偶然发现了这个问题,因为尽管我知道存在拆分应用组合,但我仍然确实需要在DataFrame上进行迭代(如问题所述)。并非每个人都可以使用numba和cython来提高奢侈度(同一文档说“首先使用Python进行优化总是值得的”)。我写这个答案是为了帮助其他人避免(有时令人沮丧)问题,因为其他答案都没有提到这些警告。误导任何人或说“这是正确的事情”绝不是我的意图。我已经改善了答案。 (3认同)

cs9*_*s95 206

如何在Pandas的DataFrame中的行上进行迭代?

答:不要!

大熊猫中的迭代是一种反模式,只有在穷尽所有其他可能选项后才应该这样做。您不应该考虑将iter名称中带有“ ”的任何函数用于超过数千行的记录,否则您将不得不习惯很多等待。

您要打印一个DataFrame吗?使用DataFrame.to_string()

您要计算吗?在这种情况下,请按以下顺序搜索方法(列表从此处修改):

  1. 向量化
  2. Cython例程
  3. 列表推导(香草for循环)
  4. DataFrame.apply():i)可以在cython中执行的约简操作,ii)在python空间中进行迭代
  5. DataFrame.itertuples()iteritems()
  6. DataFrame.iterrows()

iterrows并且itertuples(在该问题的答案中都获得很多票)应该在非常罕见的情况下使用,例如生成行对象/命名元以进行顺序处理,这实际上是这些功能唯一有用的东西。

呼吁授权迭代中
的docs页面上有一个巨大的红色警告框,指出:

遍历熊猫对象通常很慢。在许多情况下,不需要手动在行上进行迭代。


比循环快:矢量化Cython

熊猫(通过NumPy或通过Cythonized函数)对许多基本操作和计算进行了“向量化”。这包括算术,比较,(大部分)归约,整形(例如透视),联接和groupby操作。浏览有关基本基本功能的文档,以找到适合您问题的矢量化方法。

如果不存在,请使用自定义cython扩展名自行编写。


下一件事:列表理解

如果1)没有可用的向量化解决方案,2)性能很重要,但不够重要,不足以经历对代码进行cythonize的麻烦,并且3)您尝试执行元素转换,则列表理解应该是您的下一个调用端口在您的代码上。有大量证据表明,列表理解对于许多常见的熊猫任务足够快(甚至有时更快)。

公式很简单

# 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
result = [f(row[0], ..., row[n]) for row in df[['col1', ...,'coln']].values]
Run Code Online (Sandbox Code Playgroud)

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


一个明显的例子

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

在此处输入图片说明

基准测试代码,供您参考。

但是,我应该指出的是,并非总是如此。有时,“什么是最佳操作方法”的答案是“取决于您的数据”。我的建议是在建立数据之前先测试一下数据的不同方法。


参考文献

*熊猫字符串方法是“矢量化的”,因为它们在系列中已指定但可在每个元素上操作。底层机制仍然是迭代的,因为字符串操作本来就很难向量化。

  • 这是唯一针对大熊猫惯用技术的答案,这使其成为此问题的最佳答案。学会用正确的代码获得正确的答案(而不是用错误的代码获得正确的答案,即效率低下,不会)规模,太适合特定数据)是学习熊猫(以及一般数据)的重要组成部分。 (7认同)
  • 请注意,`iterrows` 和 `itertuples` 有一些重要的警告。请参阅 [此答案](/sf/answers/2871598831/) 和 [pandas 文档](https://pandas.pydata.org/pandas-docs/stable/getting_started/basics.html#iteration)更多细节。 (3认同)
  • 在列表推导式下,“迭代多列”示例需要注意:“DataFrame.values”会将每列转换为通用数据类型。`DataFrame.to_numpy()` 也可以这样做。幸运的是,我们可以将“zip”与任意数量的列一起使用。 (3认同)
  • @Dean 我经常收到这样的回复,老实说这让我很困惑。这一切都是为了养成良好的习惯。“我的数据很小,性能并不重要,所以我使用这种反模式可以原谅”..?当有一天性能确实变得重要时,您会感谢自己提前准备了正确的工具。 (3认同)
  • 但是,我认为您对for循环不公平,因为它们在我的测试中仅比列表理解慢一些。诀窍是循环遍历zip(df ['A'],df ['B'])`而不是`df.iterrows()`。 (2认同)
  • @GabrielStaples 看[这里](https://pandas.pydata.org/docs/user_guide/indexing.html#basics): _"...您可以将列列表传递给 `[]` 以选择其中的列命令。 ...”_ (2认同)

Wes*_*ney 190

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

  • 这比将DataFrame转换为numpy数组(通过.values)并直接在数组上运行更快吗?我有同样的问题,但最终转换为numpy数组,然后使用cython. (11认同)
  • @vgoklani如果逐行迭代是低效的并且你有一个非对象numpy数组,那么几乎可以肯定地使用raw numpy数组会更快,特别是对于有很多行的数组.你应该避免迭代遍历行,除非你绝对必须 (10认同)
  • 我已经对df.iterrows(),df.itertuples()和zip(df ['a'],df ['b'])的时间消耗进行了一些测试,并将结果发布在另一个的答案中问题:http://stackoverflow.com/a/34311080/2142098 (6认同)

e9t*_*e9t 144

虽然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
Run Code Online (Sandbox Code Playgroud)

  • 你能解释为什么它更快吗? (6认同)
  • 您的两个示例中的大部分时间差异似乎是由于您似乎使用.iterrows()命令的基于标签的索引和.itertuples()命令的基于整数的索引. (4认同)
  • @AbeMiessler`hiserrows()`将每行数据分成一个系列,而`itertuples()`则不是. (4认同)
  • 请注意,列的顺序实际上是不确定的,因为`df`是从字典创建的,因此`row [1]`可以引用任何列.事实证明,整数与浮动列的时间大致相同. (3认同)
  • 对于基于财务数据的数据框(时间戳和4x浮点数),迭代比我的机器上的算法快19,57倍.只有`对于a,b,c在izip中(df ["a"],df ["b"],df ["c"]:`几乎同样快. (2认同)
  • 我也获得了超过 50 倍的增长 https://i.stack.imgur.com/HBe9o.png (同时在第二次运行中更改为 attr 访问器)。 (2认同)

che*_*ard 82

您还可以使用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)
Run Code Online (Sandbox Code Playgroud)

  • 请注意,`apply`不会对行进行"iteratite",而是按行应用函数.如果您确实*需要迭代和indeces,上面的代码将不起作用,例如在比较不同行的值时(在这种情况下,除了迭代之外你什么都不做). (7认同)
  • 将轴默认设置为0是最差的 (2认同)

PJa*_*Jay 71

您可以使用df.iloc函数,如下所示:

for i in range(0, len(df)):
    print df.iloc[i]['c1'], df.iloc[i]['c2']
Run Code Online (Sandbox Code Playgroud)

  • 在`range`中使用`0`是没有意义的,你可以省略它. (17认同)
  • 如果您想保留数据类型,这是我所知道的唯一有效技术,并且还按名称引用列.`itertuples`保留了数据类型,但删除了它不喜欢的任何名称.`iterrows`则相反. (9认同)
  • 花了好几个小时试图通过大熊猫数据结构的特性来做一些简单而富有表现力的事情.这导致可读代码. (4认同)

Rom*_*ron 56

如何高效迭代

如果您真的必须迭代 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)
Run Code Online (Sandbox Code Playgroud)

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))
Run Code Online (Sandbox Code Playgroud)

2)默认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))
Run Code Online (Sandbox Code Playgroud)

3) 默认itertuples()使用 name=None 更快,但不是很方便,因为您必须为每列定义一个变量。

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))
Run Code Online (Sandbox Code Playgroud)

4) 最后,nameditertuples()比前一点慢,但是您不必为每列定义一个变量,它可以与列名一起使用,例如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))
Run Code Online (Sandbox Code Playgroud)

输出:

         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
Run Code Online (Sandbox Code Playgroud)

这篇文章是iterrows和itertuples的一个很有趣的对比


Sac*_*hin 43

我们有多种选择来做同样的事情,很多人都分享了他们的答案。

我发现以下两种方法简单有效:

  1. DataFrame.iterrows()
  2. DataFrame.itertuples()

例子:

 import pandas as pd
 inp = [{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}]
 df = pd.DataFrame(inp)
 print (df)

 # With the iterrows method

 for index, row in df.iterrows():
     print(row["c1"], row["c2"])

 # With the itertuples method

 for row in df.itertuples(index=True, name='Pandas'):
     print(row.c1, row.c2)
Run Code Online (Sandbox Code Playgroud)

注意:itertuples() 应该比 iterrows() 更快


Luc*_*s B 30

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

for i, row in df.iterrows():
    for j, column in row.iteritems():
        print(column)
Run Code Online (Sandbox Code Playgroud)


piR*_*red 17

您可以编写自己的实现迭代器 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)
Run Code Online (Sandbox Code Playgroud)

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


对于具有my function的给定数据帧:

list(myiter(df))

[MyTuple(c1=10, c2=100), MyTuple(c1=11, c2=110), MyTuple(c1=12, c2=120)]
Run Code Online (Sandbox Code Playgroud)

或者pd.DataFrame.itertuples:

list(df.itertuples(index=False))

[Pandas(c1=10, c2=100), Pandas(c1=11, c2=110), Pandas(c1=12, c2=120)]
Run Code Online (Sandbox Code Playgroud)

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

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);
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

在此输入图像描述

  • 对于不想阅读代码的人:蓝线是“ intertuples”,橙线是通过yield块的迭代器列表。不比较“ interrows”。 (2认同)

Gab*_*les 15

要点:

  1. 使用矢量化。
  2. 加速分析您的代码!不要因为你认为某件事更快而假设它更快;速度分析它并证明它更快。结果可能会让你吃惊。

如何在DataFrame不迭代的情况下迭代 Pandas

经过几周的研究这个答案,我得出了以下结论:

这里有13 种迭代 PandasDataFrame技术。正如您所看到的,所花费的时间差异很大。最快的技术比最慢的技术快约 1363 倍!正如 @cs95 所说,关键的一点是不要迭代!请改用矢量化(“数组编程”)。所有这些真正意味着您应该直接在数学公式中使用数组,而不是尝试手动迭代数组。当然,底层对象必须支持这一点,但 Numpy 和 Pandas都支持

在 Pandas 中使用矢量化的方法有很多,您可以在下面的绘图和示例代码中看到。当直接使用数组时,底层循环仍然会发生,但在(我认为)非常优化的底层 C 代码中,而不是通过原始 Python。

结果

测试了 13 种技术,编号为 1 至 13。技术编号和名称位于每个条形下方。总计算时间位于每个柱上方。下面是乘数,显示它比最右边最快的技术花费了多长时间:

来自pandas_dataframe_iteration_vs_vectorization_vs_list_comprehension_speed_tests.svg我的eRCaGuy_hello_world存储库(由此代码生成)。

在此输入图像描述

概括

列表理解向量化(可能使用布尔索引)是您真正需要的。

使用列表理解(好)和向量化(最好)。我认为纯矢量化总是可能的,但在复杂的计算中可能需要额外的工作。在此答案中搜索“布尔索引”“布尔数组”“布尔掩码”(这三个都是同一件事),以查看可以使用纯矢量化的一些更复杂的情况。

以下是 13 种技术,按从最快到最后最慢的顺序列出。我建议永远不要使用最后(最慢)的 3 到 4 种技术。

  1. 技巧8:8_pure_vectorization__with_df.loc[]_boolean_array_indexing_for_if_statment_corner_case
  2. 技巧6:6_vectorization__with_apply_for_if_statement_corner_case
  3. 技巧7:7_vectorization__with_list_comprehension_for_if_statment_corner_case
  4. 技巧11:11_list_comprehension_w_zip_and_direct_variable_assignment_calculated_in_place
  5. 技巧10:10_list_comprehension_w_zip_and_direct_variable_assignment_passed_to_func
  6. 技巧12:12_list_comprehension_w_zip_and_row_tuple_passed_to_func
  7. 技巧5:5_itertuples_in_for_loop
  8. 技巧13:13_list_comprehension_w__to_numpy__and_direct_variable_assignment_passed_to_func
  9. 技巧9:9_apply_function_with_lambda
  10. 技巧一:1_raw_for_loop_using_regular_df_indexing
  11. 技巧2:2_raw_for_loop_using_df.loc[]_indexing
  12. 技巧4:4_iterrows_in_for_loop
  13. 技巧3:3_raw_for_loop_using_df.iloc[]_indexing

经验法则:

  1. 切勿使用技术 3、4 和 2 。他们速度超级慢,没有任何优势。但请记住:并不是索引技术(例如.loc[]或 ).iloc[]使这些技术变得糟糕,而是它们所处的循环使for它们变得糟糕!.loc[]例如,我使用最快的(纯矢量化)方法!因此,这里有 3 种最慢的技术,千万不要使用:
    1. 3_raw_for_loop_using_df.iloc[]_indexing
    2. 4_iterrows_in_for_loop
    3. 2_raw_for_loop_using_df.loc[]_indexing
  2. 也不应该使用技术1_raw_for_loop_using_regular_df_indexing,但如果您要使用原始 for 循环,它比其他循环更快。
  3. 函数.apply()( 9_apply_function_with_lambda) 还可以,但一般来说,我也会避免使用它。然而,技术6_vectorization__with_apply_for_if_statement_corner_case确实表现得比 更好7_vectorization__with_list_comprehension_for_if_statment_corner_case,这很有趣。
  4. 列表理解非常棒!它不是最快的,但它易于使用并且速度非常快!
    1. 它的好处是它可以与任何旨在处理单个值或数组值的函数一起使用。if这意味着函数内部可以有非常复杂的语句和内容。因此,这里的权衡是,它通过使用外部计算函数为您提供了真正可读和可重用的代码的强大多功能性,同时仍然为您提供了很高的速度!
  5. 矢量化是最快、最好的,当方程很简单时你应该使用它。您可以选择仅对方程的更复杂部分使用类似.apply()列表理解,同时仍然可以轻松地对其余部分使用矢量化。
  6. 纯矢量化绝对是最快和最好的,如果您愿意付出努力使其发挥作用,您应该使用它。
    1. 对于简单的情况,您应该使用它。
    2. 对于复杂的情况、语句等,也可以通过布尔索引if使纯向量化起作用,但会增加额外的工作并降低可读性。因此,您可以选择仅针对那些边缘情况使用列表理解(通常是最好的)或.apply()(通常较慢,但并非总是如此),同时仍对其余计算使用向量化。例如:参见技术和。7_vectorization__with_list_comprehension_for_if_statment_corner_case6_vectorization__with_apply_for_if_statement_corner_case

测试数据

假设我们有以下 Pandas DataFrame。它有 200 万行,4 列(ABCD),每列都有从-1000到的随机值1000

df =
           A    B    C    D
0       -365  842  284 -942
1        532  416 -102  888
2        397  321 -296 -616
3       -215  879  557  895
4        857  701 -157  480
...      ...  ...  ...  ...
1999995 -101 -233 -377 -939
1999996 -989  380  917  145
1999997 -879  333 -372 -970
1999998  738  982 -743  312
1999999 -306 -103  459  745
Run Code Online (Sandbox Code Playgroud)

我这样生成了这个 DataFrame:

df =
           A    B    C    D
0       -365  842  284 -942
1        532  416 -102  888
2        397  321 -296 -616
3       -215  879  557  895
4        857  701 -157  480
...      ...  ...  ...  ...
1999995 -101 -233 -377 -939
1999996 -989  380  917  145
1999997 -879  333 -372 -970
1999998  738  982 -743  312
1999999 -306 -103  459  745
Run Code Online (Sandbox Code Playgroud)

测试方程/计算

我想证明所有这些技术都可以在非平凡的函数或方程上实现,因此我故意使他们正在计算的方程要求:

  1. if声明
  2. 来自 DataFrame 中多列的数据
  3. 来自 DataFrame 中多行的数据

我们将为每一行计算的方程是这样的。我随意编造了它,但我认为它包含足够的复杂性,您将能够扩展我所做的工作,以在完全矢量化的 Pandas 中执行您想要的任何方程:

在此输入图像描述

在Python中,上面的方程可以写成这样:

import numpy as np
import pandas as pd

# Create an array (numpy list of lists) of fake data
MIN_VAL = -1000
MAX_VAL = 1000
# NUM_ROWS = 10_000_000
NUM_ROWS = 2_000_000  # default for final tests
# NUM_ROWS = 1_000_000
# NUM_ROWS = 100_000
# NUM_ROWS = 10_000  # default for rapid development & initial tests
NUM_COLS = 4
data = np.random.randint(MIN_VAL, MAX_VAL, size=(NUM_ROWS, NUM_COLS))

# Now convert it to a Pandas DataFrame with columns named "A", "B", "C", and "D"
df_original = pd.DataFrame(data, columns=["A", "B", "C", "D"])
print(f"df = \n{df_original}")
Run Code Online (Sandbox Code Playgroud)

或者,你可以这样写:

# Calculate and return a new value, `val`, by performing the following equation:
val = (
    2 * A_i_minus_2
    + 3 * A_i_minus_1
    + 4 * A
    + 5 * A_i_plus_1
    # Python ternary operator; don't forget parentheses around the entire 
    # ternary expression!
    + ((6 * B) if B > 0 else (60 * B))
    + 7 * C
    - 8 * D
)
Run Code Online (Sandbox Code Playgroud)

其中任何一个都可以包装到一个函数中。前任:

# Calculate and return a new value, `val`, by performing the following equation:

if B > 0:
    B_new = 6 * B
else:
    B_new = 60 * B

val = (
    2 * A_i_minus_2
    + 3 * A_i_minus_1
    + 4 * A
    + 5 * A_i_plus_1
    + B_new
    + 7 * C
    - 8 * D
)
Run Code Online (Sandbox Code Playgroud)

技巧

完整的代码可以下载并在我的eRCaGuy_hello_worldpython/pandas_dataframe_iteration_vs_vectorization_vs_list_comprehension_speed_tests.py存储库中的文件中运行。

以下是所有 13 种技术的代码:

  1. 技巧一: 1_raw_for_loop_using_regular_df_indexing

    def calculate_val(
            A_i_minus_2,
            A_i_minus_1,
            A,
            A_i_plus_1,
            B,
            C,
            D):
        val = (
            2 * A_i_minus_2
            + 3 * A_i_minus_1
            + 4 * A
            + 5 * A_i_plus_1
            # Python ternary operator; don't forget parentheses around the 
            # entire ternary expression!
            + ((6 * B) if B > 0 else (60 * B))
            + 7 * C
            - 8 * D
        )
        return val
    
    Run Code Online (Sandbox Code Playgroud)
  2. 技巧2: 2_raw_for_loop_using_df.loc[]_indexing

    val = [np.NAN]*len(df)
    for i in range(len(df)):
        if i < 2 or i > len(df)-2:
            continue
    
        val[i] = calculate_val(
            df["A"][i-2],
            df["A"][i-1],
            df["A"][i],
            df["A"][i+1],
            df["B"][i],
            df["C"][i],
            df["D"][i],
        )
    df["val"] = val  # put this column back into the dataframe
    
    Run Code Online (Sandbox Code Playgroud)
  3. 技巧3: 3_raw_for_loop_using_df.iloc[]_indexing

    val = [np.NAN]*len(df)
    for i in range(len(df)):
        if i < 2 or i > len(df)-2:
            continue
    
        val[i] = calculate_val(
            df.loc[i-2, "A"],
            df.loc[i-1, "A"],
            df.loc[i,   "A"],
            df.loc[i+1, "A"],
            df.loc[i,   "B"],
            df.loc[i,   "C"],
            df.loc[i,   "D"],
        )
    
    df["val"] = val  # put this column back into the dataframe
    
    Run Code Online (Sandbox Code Playgroud)
  4. 技巧4: 4_iterrows_in_for_loop

    # column indices
    i_A = 0
    i_B = 1
    i_C = 2
    i_D = 3
    
    val = [np.NAN]*len(df)
    for i in range(len(df)):
        if i < 2 or i > len(df)-2:
            continue
    
        val[i] = calculate_val(
            df.iloc[i-2, i_A],
            df.iloc[i-1, i_A],
            df.iloc[i,   i_A],
            df.iloc[i+1, i_A],
            df.iloc[i,   i_B],
            df.iloc[i,   i_C],
            df.iloc[i,   i_D],
        )
    
    df["val"] = val  # put this column back into the dataframe
    
    Run Code Online (Sandbox Code Playgroud)

对于接下来的所有示例,我们必须首先通过添加具有上一个值和下一个值的列来准备数据框:A_(i-2)A_(i-1)A_(i+1)。DataFrame 中的这些列将分别命名为A_i_minus_2A_i_minus_1A_i_plus_1

val = [np.NAN]*len(df)
for index, row in df.iterrows():
    if index < 2 or index > len(df)-2:
        continue

    val[index] = calculate_val(
        df["A"][index-2],
        df["A"][index-1],
        row["A"],
        df["A"][index+1],
        row["B"],
        row["C"],
        row["D"],
    )

df["val"] = val  # put this column back into the dataframe
Run Code Online (Sandbox Code Playgroud)

运行上面的矢量化代码来生成这 3 个新列总共花费了0.044961 秒

现在讨论其余的技术:

  1. 技巧5: 5_itertuples_in_for_loop

    df_original["A_i_minus_2"] = df_original["A"].shift(2)  # val at index i-2
    df_original["A_i_minus_1"] = df_original["A"].shift(1)  # val at index i-1
    df_original["A_i_plus_1"] = df_original["A"].shift(-1)  # val at index i+1
    
    # Note: to ensure that no partial calculations are ever done with rows which
    # have NaN values due to the shifting, we can either drop such rows with
    # `.dropna()`, or set all values in these rows to NaN. I'll choose the latter
    # so that the stats that will be generated with the techniques below will end
    # up matching the stats which were produced by the prior techniques above. ie:
    # the number of rows will be identical to before. 
    #
    # df_original = df_original.dropna()
    df_original.iloc[:2, :] = np.NAN   # slicing operators: first two rows, 
                                       # all columns
    df_original.iloc[-1:, :] = np.NAN  # slicing operators: last row, all columns
    
    Run Code Online (Sandbox Code Playgroud)
  2. 技巧6: 6_vectorization__with_apply_for_if_statement_corner_case

    val = [np.NAN]*len(df)
    for row in df.itertuples():
        val[row.Index] = calculate_val(
            row.A_i_minus_2,
            row.A_i_minus_1,
            row.A,
            row.A_i_plus_1,
            row.B,
            row.C,
            row.D,
        )
    
    df["val"] = val  # put this column back into the dataframe
    
    Run Code Online (Sandbox Code Playgroud)
  3. 技巧7: 7_vectorization__with_list_comprehension_for_if_statment_corner_case

    def calculate_new_column_b_value(b_value):
        # Python ternary operator
        b_value_new = (6 * b_value) if b_value > 0 else (60 * b_value)  
        return b_value_new
    
    # In this particular example, since we have an embedded `if-else` statement
    # for the `B` column, pure vectorization is less intuitive. So, first we'll
    # calculate a new `B` column using
    # **`apply()`**, then we'll use vectorization for the rest.
    df["B_new"] = df["B"].apply(calculate_new_column_b_value)
    # OR (same thing, but with a lambda function instead)
    # df["B_new"] = df["B"].apply(lambda x: (6 * x) if x > 0 else (60 * x))
    
    # Now we can use vectorization for the rest. "Vectorization" in this case
    # means to simply use the column series variables in equations directly,
    # without manually iterating over them. Pandas DataFrames will handle the
    # underlying iteration automatically for you. You just focus on the math.
    df["val"] = (
        2 * df["A_i_minus_2"]
        + 3 * df["A_i_minus_1"]
        + 4 * df["A"]
        + 5 * df["A_i_plus_1"]
        + df["B_new"]
        + 7 * df["C"]
        - 8 * df["D"]
    )
    
    Run Code Online (Sandbox Code Playgroud)
  4. 技巧8: 8_pure_vectorization__with_df.loc[]_boolean_array_indexing_for_if_statment_corner_case

    这使用布尔索引(又名:布尔掩码)来完成if等式中语句的等效操作。这样,整个方程可以使用纯矢量化,从而最大限度地提高性能和速度。

    # In this particular example, since we have an embedded `if-else` statement
    # for the `B` column, pure vectorization is less intuitive. So, first we'll
    # calculate a new `B` column using **list comprehension**, then we'll use
    # vectorization for the rest.
    df["B_new"] = [
        calculate_new_column_b_value(b_value) for b_value in df["B"]
    ]
    
    # Now we can use vectorization for the rest. "Vectorization" in this case
    # means to simply use the column series variables in equations directly,
    # without manually iterating over them. Pandas DataFrames will handle the
    # underlying iteration automatically for you. You just focus on the math.
    df["val"] = (
        2 * df["A_i_minus_2"]
        + 3 * df["A_i_minus_1"]
        + 4 * df["A"]
        + 5 * df["A_i_plus_1"]
        + df["B_new"]
        + 7 * df["C"]
        - 8 * df["D"]
    )
    
    Run Code Online (Sandbox Code Playgroud)
  5. 技巧9: 9_apply_function_with_lambda

    # If statement to evaluate:
    #
    #     if B > 0:
    #         B_new = 6 * B
    #     else:
    #         B_new = 60 * B
    #
    # In this particular example, since we have an embedded `if-else` statement
    # for the `B` column, we can use some boolean array indexing through
    # `df.loc[]` for some pure vectorization magic.
    #
    # Explanation:
    #
    # Long:
    #
    # The format is: `df.loc[rows, columns]`, except in this case, the rows are
    # specified by a "boolean array" (AKA: a boolean expression, list of
    # booleans, or "boolean mask"), specifying all rows where `B` is > 0. Then,
    # only in that `B` column for those rows, set the value accordingly. After
    # we do this for where `B` is > 0, we do the same thing for where `B` 
    # is <= 0, except with the other equation.
    #
    # Short:
    #
    # For all rows where the boolean expression applies, set the column value
    # accordingly.
    #
    # GitHub CoPilot first showed me this `.loc[]` technique.
    # See also the official documentation:
    # https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.loc.html
    #
    # ===========================
    # 1st: handle the > 0 case
    # ===========================
    df["B_new"] = df.loc[df["B"] > 0, "B"] * 6
    #
    # ===========================
    # 2nd: handle the <= 0 case, merging the results into the
    # previously-created "B_new" column
    # ===========================
    # - NB: this does NOT work; it overwrites and replaces the whole "B_new"
    #   column instead:
    #
    #       df["B_new"] = df.loc[df["B"] <= 0, "B"] * 60
    #
    # This works:
    df.loc[df["B"] <= 0, "B_new"] = df.loc[df["B"] <= 0, "B"] * 60
    
    # Now use normal vectorization for the rest.
    df["val"] = (
        2 * df["A_i_minus_2"]
        + 3 * df["A_i_minus_1"]
        + 4 * df["A"]
        + 5 * df["A_i_plus_1"]
        + df["B_new"]
        + 7 * df["C"]
        - 8 * df["D"]
    )
    
    Run Code Online (Sandbox Code Playgroud)
  6. 技巧10: 10_list_comprehension_w_zip_and_direct_variable_assignment_passed_to_func

    df["val"] = df.apply(
        lambda row: calculate_val(
            row["A_i_minus_2"],
            row["A_i_minus_1"],
            row["A"],
            row["A_i_plus_1"],
            row["B"],
            row["C"],
            row["D"]
        ),
        axis='columns' # same as `axis=1`: "apply function to each row", 
                       # rather than to each column
    )
    
    Run Code Online (Sandbox Code Playgroud)
  7. 技巧11: 11_list_comprehension_w_zip_and_direct_variable_assignment_calculated_in_place

    df["val"] = [
        # Note: you *could* do the calculations directly here instead of using a
        # function call, so long as you don't have indented code blocks such as
        # sub-routines or multi-line if statements.
        #
        # I'm using a function call.
        calculate_val(
            A_i_minus_2,
            A_i_minus_1,
            A,
            A_i_plus_1,
            B,
            C,
            D
        ) for A_i_minus_2, A_i_minus_1, A, A_i_plus_1, B, C, D
        in zip(
            df["A_i_minus_2"],
            df["A_i_minus_1"],
            df["A"],
            df["A_i_plus_1"],
            df["B"],
            df["C"],
            df["D"]
        )
    ]
    
    Run Code Online (Sandbox Code Playgroud)
  8. 技巧12: 12_list_comprehension_w_zip_and_row_tuple_passed_to_func

    df["val"] = [
        2 * A_i_minus_2
        + 3 * A_i_minus_1
        + 4 * A
        + 5 * A_i_plus_1
        # Python ternary operator; don't forget parentheses around the entire
        # ternary expression!
        + ((6 * B) if B > 0 else (60 * B))
        + 7 * C
        - 8 * D
        for A_i_minus_2, A_i_minus_1, A, A_i_plus_1, B, C, D
        in zip(
            df["A_i_minus_2"],
            df["A_i_minus_1"],
            df["A"],
            df["A_i_plus_1"],
            df["B"],
            df["C"],
            df["D"]
        )
    ]
    
    Run Code Online (Sandbox Code Playgroud)
  9. 技巧13: 13_list_comprehension_w__to_numpy__and_direct_variable_assignment_passed_to_func

    df["val"] = [
        # Note: you *could* do the calculations directly here instead of using a
        # function call, so long as you don't have indented code blocks such as
        # sub-routines or multi-line if statements.
        #
        # I'm using a function call.
        calculate_val(
            A_i_minus_2,
            A_i_minus_1,
            A,
            A_i_plus_1,
            B,
            C,
            D
        ) for A_i_minus_2, A_i_minus_1, A, A_i_plus_1, B, C, D
            # Note: this `[[...]]` double-bracket indexing is used to select a
            # subset of columns from the dataframe. The inner `[]` brackets
            # create a list from the column names within them, and the outer 
            # `[]` brackets accept this list to index into the dataframe and
            # select just this list of columns, in that order.
            # - See the official documentation on it here:
            #   https://pandas.pydata.org/docs/user_guide/indexing.html#basics
            #   - Search for the phrase "You can pass a list of columns to [] to
            #     select columns in that order."
            #   - I learned this from this comme


Ped*_*ito 14

要遍历a中的所有行,dataframe您可以使用:

for x in range(len(date_example.index)):
    print date_example['Date'].iloc[x]
Run Code Online (Sandbox Code Playgroud)


Gra*_*015 14

恕我直言,最简单的决定

 for ind in df.index:
     print df['c1'][ind], df['c2'][ind]
Run Code Online (Sandbox Code Playgroud)

  • 这是链接索引。不要使用这个! (2认同)

Joh*_*hnE 13

有时循环确实比矢量化代码更好

正如这里的许多答案正确指出的那样,Pandas 中的默认计划应该是编写矢量化代码(及其隐式循环),而不是自己尝试显式循环。但问题仍然是您是否应该Pandas 中编写循环,如果是的话,在这些情况下循环的最佳方法是什么。

我相信至少在一种一般情况下循环是合适的:当您需要以某种复杂的方式计算某个依赖于其他行中的值的函数时。在这种情况下,循环代码通常比矢量化代码更简单、更易读并且更不容易出错。

循环代码甚至可能更快,如下所示,因此在速度至关重要的情况下循环可能有意义。但实际上,这些只是您可能应该一开始就使用 numpy/numba(而不是 Pandas)的情况的子集,因为优化的 numpy/numba 几乎总是比 Pandas 更快。

让我们用一个例子来展示这一点。假设您想要获取一列的累积和,但只要其他列等于零就重置它:

import pandas as pd
import numpy as np

df = pd.DataFrame( { 'x':[1,2,3,4,5,6], 'y':[1,1,1,0,1,1]  } )

#   x  y  desired_result
#0  1  1               1
#1  2  1               3
#2  3  1               6
#3  4  0               4
#4  5  1               9
#5  6  1              15
Run Code Online (Sandbox Code Playgroud)

这是一个很好的例子,你当然可以编写一行 Pandas 来实现这一点,尽管它不是特别可读,特别是如果你对 Pandas 还没有相当的经验的话:

df.groupby( (df.y==0).cumsum() )['x'].cumsum()
Run Code Online (Sandbox Code Playgroud)

对于大多数情况来说,这已经足够快了,尽管您也可以通过避免使用 来编写更快的代码groupby,但它的可读性可能会更差。

或者,如果我们把它写成循环怎么办?您可以使用 NumPy 执行类似以下操作:

import numba as nb

@nb.jit(nopython=True)  # Optional
def custom_sum(x,y):
    x_sum = x.copy()
    for i in range(1,len(df)):
        if y[i] > 0: x_sum[i] = x_sum[i-1] + x[i]
    return x_sum

df['desired_result'] = custom_sum( df.x.to_numpy(), df.y.to_numpy() )
Run Code Online (Sandbox Code Playgroud)

诚然,将 DataFrame 列转换为 NumPy 数组需要一些开销,但核心代码只是一行代码,即使您对 Pandas 或 NumPy 一无所知,您也可以阅读它:

if y[i] > 0: x_sum[i] = x_sum[i-1] + x[i]
Run Code Online (Sandbox Code Playgroud)

而且这个代码实际上比矢量化代码更快。在一些 100,000 行的快速测试中,上述方法比groupby方法快大约 10 倍。请注意,速度的关键是 numba,这是可选的。如果没有“@nb.jit”行,循环代码实际上比groupby方法慢大约 10 倍。

显然,这个示例非常简单,您可能更喜欢一行 pandas,而不是编写一个具有相关开销的循环。然而,这个问题还有更复杂的版本,对于这些版本,NumPy/numba 循环方法的可读性或速度可能是有意义的。


Jor*_*rdy 13

我建议使用df.at[row, column]( source ) 来迭代所有 pandas 单元。

例如 :

for row in range(len(df)):
  print(df.at[row, 'c1'], df.at[row, 'c2'])
Run Code Online (Sandbox Code Playgroud)

输出将是:

10 100
11 110
12 120
Run Code Online (Sandbox Code Playgroud)

奖金

您还可以使用 修改单元格的值df.at[row, column] = newValue

for row in range(len(df)):
  print(df.at[row, 'c1'], df.at[row, 'c2'])
Run Code Online (Sandbox Code Playgroud)

输出将是:

data-10 100
data-11 110
data-12 120
Run Code Online (Sandbox Code Playgroud)


art*_*oby 12

简而言之

  • 如果可能,使用矢量化
  • 如果操作无法矢量化 - 使用列表推导式
  • 如果您需要代表整行的单个对象 - 使用 itertuples
  • 如果以上太慢 - 尝试swifter.apply
  • 如果它仍然太慢 - 尝试Cython例程

基准

Pandas DataFrame 中行的迭代基准


Hos*_*ein 10

对于查看和修改值,我会使用iterrows(). 在 for 循环中并通过使用元组解包(参见示例:)i, row,我使用rowfor 仅查看值并在我想修改值时iloc方法一起使用。正如之前的答案所述,在这里您不应该修改您正在迭代的内容。

for i, row in df.iterrows():
    df_column_A = df.loc[i, 'A']
    if df_column_A == 'Old_Value':
        df_column_A = 'New_value'  
Run Code Online (Sandbox Code Playgroud)

这里的rowin 循环是该行的副本,而不是它的视图。因此,你不应该写类似的东西row['A'] = 'New_Value',它不会修改数据帧。但是,您可以使用iloc指定 DataFrame 来完成这项工作。


Zei*_*ist 10

有一种方法可以在获取 DataFrame 的同时迭代抛出行,而不是 Series。我没有看到有人提到您可以将索引作为列表传递给要作为 DataFrame 返回的行:

for i in range(len(df)):
    row = df.iloc[[i]]
Run Code Online (Sandbox Code Playgroud)

注意双括号的使用。这将返回一个带有单行的 DataFrame。


And*_*rdo 10

更新:cs95 更新了他的答案,以包含普通的 numpy 向量化。你可以简单地参考他的回答。


cs95 表明Pandas 向量化在计算数据帧内容方面远远优于其他 Pandas 方法。

我想补充一点,如果您首先将数据帧转换为 NumPy 数组,然后使用矢量化,它甚至比 Pandas 数据帧矢量化还要快(这包括将其转换回数据帧系列的时间)。

如果将以下函数添加到 cs95 的基准代码中,这将变得非常明显:

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]
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

  • [cs95的基准测试代码,供您参考](https://gist.github.com/Coldsp33d/948f96b384ca5bdf6e8ce203ac97c9a0/revisions) (2认同)

Ern*_*ßer 8

可能是最优雅的解决方案(但肯定不是最有效的):

for row in df.values:
    c2 = row[1]
    print(row)
    # ...

for c1, c2 in df.values:
    # ...
Run Code Online (Sandbox Code Playgroud)

注意:

  • 该文档.to_numpy()明确建议使用
  • 在最坏的情况下,生成的 NumPy 数组将具有适合所有列的 dtypeobject
  • 一开始就不使用循环是有充分理由的

尽管如此,我认为这个选项应该包含在此处,作为解决(人们应该认为的)琐碎问题的直接解决方案。


Zac*_*ach 7

除了上面的答案,有时一个有用的模式是:

# Borrowing @KutalmisB df example
df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b'])
# The to_dict call results in a list of dicts
# where each row_dict is a dictionary with k:v pairs of columns:value for that row
for row_dict in df.to_dict(orient='records'):
    print(row_dict)
Run Code Online (Sandbox Code Playgroud)

结果如下:

{'col1':1.0, 'col2':0.1}
{'col1':2.0, 'col2':0.2}
Run Code Online (Sandbox Code Playgroud)


小智 7

有很多方法可以迭代 Pandas 数据帧中的行。一种非常简单直观的方法是:

df = pd.DataFrame({'A':[1, 2, 3], 'B':[4, 5, 6], 'C':[7, 8, 9]})
print(df)
for i in range(df.shape[0]):
    # For printing the second column
    print(df.iloc[i, 1])

    # For printing more than one columns
    print(df.iloc[i, [0, 2]])
Run Code Online (Sandbox Code Playgroud)


Jam*_* L. 6

您还可以进行 NumPy 索引以获得更高的速度。它并不是真正的迭代,但对于某些应用程序来说比迭代效果更好。

subset = row['c1'][0:5]
all = row['c1'][:]
Run Code Online (Sandbox Code Playgroud)

您可能还想将其转换为数组。这些索引/选择应该像 NumPy 数组一样工作,但我遇到了问题并且需要强制转换

np.asarray(all)
imgs[:] = cv2.resize(imgs[:], (224,224) ) # Resize every image in an hdf5 file
Run Code Online (Sandbox Code Playgroud)


Her*_*eer 6

要循环使用 a中的所有行dataframe方便地使用每行的值,可以将其转换为s.例如:namedtuplesndarray

df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b'])
Run Code Online (Sandbox Code Playgroud)

迭代行:

for row in df.itertuples(index=False, name='Pandas'):
    print np.asarray(row)
Run Code Online (Sandbox Code Playgroud)

结果是:

[ 1.   0.1]
[ 2.   0.2]
Run Code Online (Sandbox Code Playgroud)

请注意,如果index=True,索引被添加为元组的第一个元素,这对于某些应用程序可能是不合需要的.


Fra*_* B. 5

最简单的方法,使用apply函数

def print_row(row):
   print row['c1'], row['c2']

df.apply(lambda row: print_row(row), axis=1)
Run Code Online (Sandbox Code Playgroud)


小智 5

df.iterrows()返回tuple(a, b)其中aindexbrow


cot*_*ail 5

1. 迭代df.index并访问通过at[]

一种非常可读的方法是迭代索引(如@Grag2015建议)。但是,为了提高效率,不要使用链式索引at

for ind in df.index:
    print(df.at[ind, 'col A'])
Run Code Online (Sandbox Code Playgroud)

此方法的优点for i in range(len(df))是即使索引不正确,它也能工作RangeIndex。请参见以下示例:

df = pd.DataFrame({'col A': list('ABCDE'), 'col B': range(5)}, index=list('abcde'))

for ind in df.index:
    print(df.at[ind, 'col A'], df.at[ind, 'col B'])    # <---- OK
    df.at[ind, 'col C'] = df.at[ind, 'col B'] * 2      # <---- can assign values
        
for ind in range(len(df)):
    print(df.at[ind, 'col A'], df.at[ind, 'col B'])    # <---- KeyError
Run Code Online (Sandbox Code Playgroud)

如果需要一行的整数位置(例如,获取前一行的值),请将其包装为enumerate()

for i, ind in enumerate(df.index):
    prev_row_ind = df.index[i-1] if i > 0 else df.index[i]
    df.at[ind, 'col C'] = df.at[prev_row_ind, 'col B'] * 2
Run Code Online (Sandbox Code Playgroud)

2.get_loc配合使用itertuples()

尽管它比 快得多iterrows(),但 的一个主要缺点itertuples()是,如果列标签中包含空格(例如'col C'变成_1等),它会破坏列标签,这使得在迭代中访问值变得困难。

您可以使用df.columns.get_loc()获取列标签的整数位置并使用它来索引命名元组。请注意,每个命名元组的第一个元素是索引标签,因此要通过整数位置正确访问列,您必须向返回的内容加 1,get_loc或者在开头解压元组。

df = pd.DataFrame({'col A': list('ABCDE'), 'col B': range(5)}, index=list('abcde'))

for row in df.itertuples(name=None):
    pos = df.columns.get_loc('col B') + 1              # <---- add 1 here
    print(row[pos])


for ind, *row in df.itertuples(name=None):
#   ^^^^^^^^^    <---- unpacked here
    pos = df.columns.get_loc('col B')                  # <---- already unpacked
    df.at[ind, 'col C'] = row[pos] * 2
    print(row[pos])
Run Code Online (Sandbox Code Playgroud)

3. 转换为字典并迭代dict_items

循环数据帧的另一种方法是将其转换为字典 inorient='index'并迭代dict_itemsor dict_values

df = pd.DataFrame({'col A': list('ABCDE'), 'col B': range(5)})

for row in df.to_dict('index').values():
#                             ^^^^^^^^^         <--- iterate over dict_values
    print(row['col A'], row['col B'])


for index, row in df.to_dict('index').items():
#                                    ^^^^^^^^   <--- iterate over dict_items
    df.at[index, 'col A'] = row['col A'] + str(row['col B'])
Run Code Online (Sandbox Code Playgroud)

这不会破坏像这样的数据类型iterrows,不会破坏像这样的列标签,itertuples并且不知道列数(zip(df['col A'], df['col B'], ...)如果有很多列,很快就会变得很麻烦)。


最后,作为@cs95提到的,尽可能避免循环。特别是如果您的数据是数字,如果您稍微挖掘一下,库中就会有针对您的任务的优化方法。

也就是说,在某些情况下迭代比矢量化操作更有效。一个常见的此类任务是将 pandas 数据帧转储到嵌套的 json 中。至少从 pandas 1.5.3 开始,在这种情况下,itertuples()循环比涉及方法的任何向量化操作要快得多。groupby.apply


归档时间:

查看次数:

1987524 次

最近记录:

6 年 前