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


当前回答

iterrows()返回元组(a,b),其中a是索引,b是行。

其他回答

您还可以使用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)

除了这篇文章中的好答案,我将提出“分而治之”的方法,我写这个答案并不是为了废除其他好答案,而是为了用另一种对我有效的方法来实现它们。它有两个步骤,即拆分和合并熊猫数据帧:

分裂与征服的证明:

您不需要使用矢量化或任何其他方法将数据帧的类型转换为另一种类型您不需要将代码循环化,这通常需要额外的时间在我的例子中,iterrows()和itertples()在整个数据帧上都具有相同的性能根据您对切片索引的选择,您将能够以指数方式加快迭代。索引越高,迭代过程越快。

分裂与征服的缺点:

您不应该依赖于同一数据帧和不同切片的迭代过程。这意味着,如果你想从其他切片读取或写入,那么可能很难做到这一点。

===============分而治之=================

步骤1:分割/切片

在这一步中,我们将在整个数据帧上划分迭代。假设你要将一个CSV文件读入panda df,然后对其进行迭代。在这种情况下,我有5000000条记录,我要将其拆分为100000条记录。

注意:我需要重申,正如本页其他解决方案中解释的其他运行时分析一样,在df上搜索时,“记录数”与“运行时”成指数比例。基于我的数据基准,以下是结果:

Number of records | Iteration rate [per second]
========================================
100,000           | 500
500,000           | 200
1,000,000         | 50
5,000,000         | 20

第2步:合并

这将是一个简单的步骤,只需将所有写入的CSV文件合并到一个数据帧中,然后将其写入一个更大的CSV文件。

以下是示例代码:

# Step 1 (Splitting/Slicing)
import pandas as pd
df_all = pd.read_csv('C:/KtV.csv')
df_index = 100000
df_len = len(df)
for i in range(df_len // df_index + 1):
    lower_bound = i * df_index
    higher_bound = min(lower_bound + df_index, df_len)
    # Splitting/slicing df (make sure to copy() otherwise it will be a view
    df = df_all[lower_bound:higher_bound].copy()
    '''
    Write your iteration over the sliced df here
    using iterrows() or intertuples() or ...
    '''
    # Writing into CSV files
    df.to_csv('C:/KtV_prep_' + str(i) + '.csv')



# Step 2 (Merging)
filename = 'C:/KtV_prep_'
df = (pd.read_csv(f) for f in [filename + str(i) + '.csv' for i in range(ktv_len // ktv_index + 1)])
df_prep_all = pd.concat(df)
df_prep_all.to_csv('C:/KtV_prep_all.csv')

参考:

数据流迭代的有效方法

将CSV文件连接到一个Pandas数据帧中

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

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

df.apply(lambda row: print_row(row), axis=1)
 for ind in df.index:
     print df['c1'][ind], df['c2'][ind]

如何在Pandas中迭代DataFrame中的行

回答:不要*!

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

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

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

矢量化Cython例程列出理解(循环的香草)DataFrame.apply():i)可以在Cython中执行的缩减,ii)Python空间中的迭代DataFrame.itertuples()和itertimes()DataFrame.iterrows()

iterrows和itertules(都在回答这个问题时获得了很多投票)应该在非常罕见的情况下使用,例如为顺序处理生成行对象/nametuples,这实际上是这些函数唯一有用的功能。

向当局提出上诉

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

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

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


比循环更快:矢量化,Cython

大量的基本操作和计算都由panda“矢量化”(通过NumPy或通过Cythonized函数)。这包括算术、比较、(大多数)归约、重塑(如旋转)、联接和分组操作。查看有关基本功能的文档,找到适合您问题的矢量化方法。

如果没有,请使用自定义Cython扩展编写自己的代码。


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

如果1)没有可用的矢量化解决方案,2)性能很重要,但还不够重要,不足以解决代码代码化的麻烦,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 - 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时,如果存在内置panda方法(因为它们具有更好的角案例处理逻辑),请选择内置panda,或者确保您的业务逻辑包含适当的NaN处理逻辑。当处理混合数据类型时,应该迭代zip(df['A'],df['B'],…)而不是df[['A','B'].to_num(),因为后者隐式地将数据上转换为最常见的类型。例如,如果A是数字,B是字符串,to_numpy()将整个数组强制转换为字符串,这可能不是您想要的。幸运的是,将您的专栏压缩在一起是解决此问题的最直接的方法。

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


一个明显的例子

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

基准测试代码,供您参考。底部的一行衡量的是用numpanda编写的函数,这是一种Pandas风格,它与NumPy大量混合,以挤出最大的性能。除非你知道自己在做什么,否则应该避免编写numpanda代码。尽可能使用API(即,更喜欢vec而不是vec_numpy)。

然而,我应该提一提,这并不总是那么简单。有时候,“手术的最佳方法是什么”的答案是“这取决于你的数据”。我的建议是在确定数据之前,先测试不同的方法。


我的个人意见*

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

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

对于新手:

矢量化(如果可能);apply();列出理解;itertples()/iteritems();迭代();赛龙

对于更有经验的人:

矢量化(如果可能);apply();列出理解;Cython;itertples()/iteritems();迭代()

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

我确实倾向于在我的很多帖子中继续谈论申请有多糟糕,但我承认初学者更容易理解申请的内容。此外,在我的这篇文章中解释了许多应用的用例。

Cython在榜单上排名靠后,因为它需要更多的时间和精力才能正确完成。通常情况下,您永远不需要使用panda编写代码,因为它需要这样的性能水平,即使是列表理解也无法满足。

*与任何个人意见一样,请带着大量的盐!


进一步阅读

熊猫10分钟,以及基本功能-有用的链接,可向您介绍熊猫及其矢量化*/细胞化函数库。提高性能-从提高标准熊猫操作的文档中获得的入门知识熊猫的循环真的很糟糕吗?我什么时候该关心?-我对列表的理解及其对各种操作的适用性(主要是涉及非数字数据的操作)的详细记录我什么时候应该(不)在代码中使用pandas apply()?-apply很慢(但没有iter*家族那么慢。但是,在某些情况下,可以(或应该)将apply视为一种严肃的选择,特别是在某些GroupBy操作中)。

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


为什么我写这个答案

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

这个答案的目的是帮助新用户理解迭代不一定是每个问题的解决方案,可以存在更好、更快、更地道的解决方案。我并不是想挑起一场迭代与矢量化的战争,但我希望新用户在使用这个库开发解决问题的解决方案时得到通知。