我有一个熊猫数据帧,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()。在for循环中,通过使用元组解包(参见示例:i,row),我使用行仅查看值,并在需要修改值时使用i和loc方法。正如前面的回答所述,这里您不应该修改正在迭代的内容。

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

在这里,循环中的行是该行的副本,而不是它的视图。因此,您不应该编写类似于行['a']='New_Value'的内容,它不会修改DataFrame。但是,您可以使用i和loc并指定DataFrame来完成这项工作。

其他回答

您可以编写自己的迭代器来实现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);

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

对于查看和修改值,我将使用iterrows()。在for循环中,通过使用元组解包(参见示例:i,row),我使用行仅查看值,并在需要修改值时使用i和loc方法。正如前面的回答所述,这里您不应该修改正在迭代的内容。

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

在这里,循环中的行是该行的副本,而不是它的视图。因此,您不应该编写类似于行['a']='New_Value'的内容,它不会修改DataFrame。但是,您可以使用i和loc并指定DataFrame来完成这项工作。

为了循环数据帧中的所有行并方便地使用每行的值,可以将命名元组转换为ndarray。例如:

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

在行上循环:

for row in df.itertuples(index=False, name='Pandas'):
    print np.asarray(row)

结果是:

[ 1.   0.1]
[ 2.   0.2]

请注意,如果index=True,则将索引添加为元组的第一个元素,这对于某些应用程序来说可能是不可取的。

如何在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上迭代,他们做的第一件事就是谷歌搜索它,并在这里结束这个问题。然后,他们看到被接受的答案,告诉他们如何去做,然后闭上眼睛运行这段代码,而不必首先质疑迭代是否正确。

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