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

我有一个来自熊猫的DataFrame

import pandas as pd
inp = [{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}]
df = pd.DataFrame(inp)
print 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对象以及如何使用它。

答案

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}])

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

Output: 
   10 100
   11 110
   12 120

首先考虑是否真的需要遍历 DataFrame 中的行。有关其他选择,请参见此答案

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

itertuples()应该比iterrows()更快

但是要注意,根据文档(目前为熊猫 0.24.2):

  • iterrows: dtype可能不符合行与行

    因为 iterrows 为每一行返回一个 Series,所以它不会在各行中保留 dtype(dtypes 在 DataFrame 的各列之间都保留)。为了在遍历行时保留 dtype,最好使用 itertuples()返回值的 namedtuple,通常比 iterrows()快得多

  • 行:不修改行

    永远不应修改要迭代的内容。不能保证在所有情况下都能正常工作。根据数据类型,迭代器将返回副本而不是视图,并且对其进行写入将无效。

    使用DataFrame.apply()代替:

    new_df = df.apply(lambda x: x * 2)
  • itertuples:

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

有关更多详细信息,请参见有关迭代的 pandas 文档

如何在 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()

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

向当局提出上诉
迭代的文档页面上有一个巨大的红色警告框,其中指出:

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


比循环快: 矢量化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]

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


一个明显的例子

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

在此处输入图片说明

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

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


参考文献

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

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

尽管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

您还可以使用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.iloc 函数:

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

我一直在寻找如何在行和列上进行迭代,因此在这里结束:

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

您可以编写自己的实现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进行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);

在此处输入图片说明

在此处输入图片说明

for ind in df.index:
     print df['c1'][ind], df['c2'][ind]