如何在Pandas中迭代DataFrame中的行

4017

我有一个Pandas数据框,df

   c1   c2
0  10  100
1  11  110
2  12  120

我该如何迭代遍历这个数据框的行?对于每一行,我想通过列名访问其元素(单元格中的值)。例如:
for row in df.rows:
    print(row['c1'], row['c2'])

我找到了一个类似的问题,建议使用以下任一方法:

  • 对于日期和行数据,使用df.T.iteritems()进行循环:
    
  • 使用df.iterrows()进行循环:
    

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


29
df.iteritems() 迭代的是列而不是行,因此想要迭代行,需要转置(使用“T”操作),将行和列互换位置(对角线反转)。这样,使用 df.T.iteritems() 时,你会有效地遍历原始数据框的每一行。 - Stefan Gruenwald
169
与cs95所说的相反,有很好的理由想要遍历一个数据框,因此新用户不应感到气馁。一个例子是如果您想使用每行的值作为输入来执行一些代码。此外,如果您的数据框相当小(例如少于1000个项目),性能实际上不是问题。 - oulenz
6
在Python中,数据框似乎是默认的表格格式。因此,无论您想读取CSV文件,还是有一个字典列表需要操作其值,或者您想执行简单的连接、分组或窗口操作,都可以使用数据框,即使您的数据相对较小也是如此。 - oulenz
37
我同意@oulenz的观点。据我所知,即使数据集很小,pandas也是读取csv文件的首选。使用API来操作数据更加易于编程。 - F.S.
9
如果您是这个线程的初学者,并且不熟悉pandas库,那么值得退一步评估迭代是否确实是解决问题的方法。在某些情况下,它是有效的。但在大多数情况下,它并不是最佳选择。重要的是通过向他们介绍向量化的概念来帮助初学者了解如何编写“好代码”和“只是能运行的代码”的区别,以及何时使用哪种方法。 - cs95
显示剩余6条评论
34个回答

3

一些库(例如我使用的Java互操作库)要求一次传递一行值,例如,如果是流数据。为了复制流式特性,我逐个“流”我的数据框架值,我写了下面的代码,这在某些时候非常有用。

class DataFrameReader:
  def __init__(self, df):
    self._df = df
    self._row = None
    self._columns = df.columns.tolist()
    self.reset()
    self.row_index = 0

  def __getattr__(self, key):
    return self.__getitem__(key)

  def read(self) -> bool:
    self._row = next(self._iterator, None)
    self.row_index += 1
    return self._row is not None

  def columns(self):
    return self._columns

  def reset(self) -> None:
    self._iterator = self._df.itertuples()

  def get_index(self):
    return self._row[0]

  def index(self):
    return self._row[0]

  def to_dict(self, columns: List[str] = None):
    return self.row(columns=columns)

  def tolist(self, cols) -> List[object]:
    return [self.__getitem__(c) for c in cols]

  def row(self, columns: List[str] = None) -> Dict[str, object]:
    cols = set(self._columns if columns is None else columns)
    return {c : self.__getitem__(c) for c in self._columns if c in cols}

  def __getitem__(self, key) -> object:
    # the df index of the row is at index 0
    try:
        if type(key) is list:
            ix = [self._columns.index(key) + 1 for k in key]
        else:
            ix = self._columns.index(key) + 1
        return self._row[ix]
    except BaseException as e:
        return None

  def __next__(self) -> 'DataFrameReader':
    if self.read():
        return self
    else:
        raise StopIteration

  def __iter__(self) -> 'DataFrameReader':
    return self

可以使用哪些:

for row in DataFrameReader(df):
  print(row.my_column_name)
  print(row.to_dict())
  print(row['my_column_name'])
  print(row.tolist())

并保留正在迭代的行的值/名称映射。显然,与上面提到的使用apply和Cython相比,速度要慢得多,但在某些情况下是必要的。


2
正如被接受的答案所述,以最快的速度在行上应用函数的方法是使用向量化函数,即所谓的NumPy ufuncs(通用函数)。但是,如果您想要应用的函数尚未在NumPy中实现,该怎么办呢?嗯,在Python中,您可以使用numbavectorize装饰器轻松创建ufuncs,就像这样:
from numba import vectorize, float64

@vectorize([float64(float64)])
def f(x):
    #x is your line, do something with it, and return a float

该函数的文档在这里:创建NumPy通用函数

2
除了本帖中的众多优秀答案外,我要提出“分而治之”的方法。我写这篇答案并非要废除其他优秀答案,而是想用另一种方法来补充它们,这种方法对我来说效率很高。它有两个步骤:拆分和合并pandas数据框。
“分而治之”方法的优点:
- 你不需要使用向量化或任何其他方法将数据框的类型转换为另一种类型。 - 你不需要将代码Cython化,这通常会花费额外的时间。 - 在我的情况下,`iterrows()`和`itertuples()`两者在整个数据框上的性能相同。 - 取决于你选择切片的索引,你将能够指数级加快迭代速度。索引越高,迭代过程就越快。
“分而治之”方法的缺点:
- 你不应该依赖于迭代过程对同一数据框和不同的“切片”。也就是说,如果你想从其他“切片”读取或写入内容,可能会很困难。

=================== 分治法 =================

步骤1:分割/切片

在这个步骤中,我们将对整个数据框进行迭代的划分。想象一下,您将把 CSV 文件读入 Pandas 数据框中,然后进行迭代。在我的案例中,我有 5,000,000 条记录,我将把它分成 100,000 条记录。

注意: 我需要再次说明,根据本页其他解决方案所解释的其他运行时分析,“记录数”与 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')

参考资料:

高效迭代DataFrame数据的方法

将CSV文件合并为一个Pandas DataFrame


你为什么认为这种方法比直接在整个数据框上进行iterrows更快?它具有相同的内存和时间复杂度,并且有更多的操作(特别是csv io应该非常慢)。而且,严格来说,这并不是一个分而治之的方法。分而治之是一种递归范式。你只是将你的数据框切分成df_index大小的子表,并使用这些子表。 - undefined

0
使用`df.iloc[]`。例如,使用数据框'rows_df':

Enter image description here

或者

要从特定行获取值,您可以将数据框转换为ndarray。

然后像这样选择行和列的值:

Enter image description here


14
请勿将代码以图片形式发布,而应该使用代码块以纯文本的格式呈现。 - Scratte

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接