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

时间:2013-05-10 07:04:50

标签: python pandas 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

输出:

   c1   c2
0  10  100
1  11  110
2  12  120

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

for row in df.rows:
   print row['c1'], row['c2']

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

我发现了similar question。但它没有给我我需要的答案。例如,建议使用:

for date, row in df.T.iteritems():

for row in df.iterrows():

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

29 个答案:

答案 0 :(得分:1744)

DataFrame.iterrows是一个产生索引和行

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

Output: 
   10 100
   11 110
   12 120

答案 1 :(得分:270)

首先考虑一下你是否真的需要迭代来覆盖DataFrame中的行。有关替代方案,请参阅this answer

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

itertuples()应该比iterrows()

更快

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

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

      

    因为iterrows为每一行返回一个Series,所以不会在行中保留 dtypes(dtypes在DataFrames的列之间保留)。为了在迭代行时保留dtypes,最好使用itertuples()返回值的namedtuples,它通常比iterrows快得多()

  • iterrows:不要修改行

      

    您应该永远不会修改您正在迭代的内容。这并不能保证在所有情况下都有效。根据数据类型,迭代器返回一个副本而不是视图,写入它将不起作用。

    改为使用DataFrame.apply()

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

      

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

有关详细信息,请参阅pandas docs on iteration

答案 2 :(得分:140)

虽然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

答案 3 :(得分:78)

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

答案 4 :(得分:66)

您可以按如下方式使用df.iloc函数:

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

答案 5 :(得分:34)

  

问:如何在Pandas中的DataFrame中遍历行?

不要!

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

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

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

  1. 向量化
  2. Cython例程
  3. 列表理解(for循环)
  4. DataFrame.apply()
    一世。可在cython中执行的还原操作
    ii。 python空间中的迭代
  5. DataFrame.itertuples()iteritems()
  6. DataFrame.iterrows()

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

向当局提出上诉
The docs page在迭代中有一个巨大的红色警告框,上面显示:

  

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


比循环更快:VectorizationCython

大熊猫(通过NumPy或Cythonized函数)将“大量矢量化”了基本操作和计算。这包括算术,比较,(大多数)归约,整形(例如透视),联接和groupby操作。浏览Essential Basic Functionality上的文档以找到适合您问题的矢量化方法。

如果不存在,请随时使用自定义cython extensions编写自己的内容。


下一件事:List Comprehensions

如果由于没有可用的向量化解决方案而进行迭代,并且性能很重要(但不足以使代码被cython化),则使用列表理解作为下一个最佳/最简单的选择。

要使用单个列遍历行,请使用

result = [f(x) for x in df['col']]

要遍历使用多列的行,可以使用

# two column format
result = [f(x, y) for x, y in zip(df['col1'], df['col2'])]

# many column format
result = [f(row[0], ..., row[n]) for row in df[['col1', ...,'coln']].values]

如果在迭代时需要整数行索引,请使用enumerate

result = [f(...) for i, row in enumerate(df[...].values)]

(其中df.index[i]为您提供索引标签。)

如果可以将其转换为函数,则可以使用列表推导。您可以通过原始python的简单性和速度来使任意复杂的事情起作用。

答案 6 :(得分:26)

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

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

答案 7 :(得分:15)

您可以编写自己的迭代器来实现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直接相当。我的目标是以更高的效率执行相同的任务。

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

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);

enter image description here

enter image description here

答案 8 :(得分:13)

要循环dataframe中的所有行,您可以使用:

for x in range(len(date_example.index)):
    print date_example['Date'].iloc[x]

答案 9 :(得分:12)

const

答案 10 :(得分: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)

这将导致:

{'col1':1.0, 'col2':0.1}
{'col1':2.0, 'col2':0.2}

答案 11 :(得分:6)

要循环<{1}}和使用每行的所有行方便dataframe可以转换为namedtuples秒。例如:

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] 将索引添加为元组的第一个元素,这对某些应用程序可能不合适。

答案 12 :(得分:6)

简而言之

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

基准 Benchmark of iteration over rows in a pandas DataFrame

答案 13 :(得分:6)

cs95 shows,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]

Enter image description here

答案 14 :(得分:4)

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

for i in range(len(df)):
    row = df.iloc[[i]]

请注意双括号的用法。这将返回一个具有单行的DataFrame。

答案 15 :(得分:2)

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

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

df.apply(lambda row: print_row(row), axis=1)

答案 16 :(得分:2)

我试图弄清楚如何通过熊猫数据框有效地进行迭代。有不同的方法,通常的iterrows()远非最佳。 itertuples()可以快100倍。

df = pd.DataFrame(np.random.randint(0, 100, size=(1000000, 4)), columns=list('ABCD'))
print(df)

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

2)默认的itertuples()已经快了

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

3)使用另一种访问方法的默认itertuples()

start_time = time.clock()
result = 0
for row in df.itertuples():
    result += max(getattr(row, "B"), getattr(row, "C"))

total_elapsed_time = round(time.clock() - start_time, 2)
print("3. Named Itertuples using getattr done in {} seconds, result = {}".format(total_elapsed_time, result))

4)使用name = None的默认itertuples()更快

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("4. Itertuples done in {} seconds, result = {}".format(total_elapsed_time, result))

5)命名为itertuples()的列名,例如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("5. Polyvalent Itertuples working even with special characters in the column name done in {} seconds, result = {}".format(total_elapsed_time, result))

输出:

         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. Named Itertuples using getattr done in 1.56 seconds, result = 66151519
4. Itertuples done in 0.94 seconds, result = 66151519
5. Polyvalent Itertuples working even with special characters in the column name done in 2.94 seconds, result = 66151519

This article is a very interesting comparison between iterrows and itertuples

总结:

  • 列数固定(且少于255个列)时,请使用df.itertuples(name=None)
  • 使用df.itertuples()否则,除非您的列中有特殊字符(''或'-',)
  • 通过最后一个示例,即使您的数据框具有奇怪的列,也可以使用itertuples()
  • 仅在无法解决以前的解决方案时使用iterrows()

答案 17 :(得分:1)

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

答案 18 :(得分:1)

在这篇文章中,除了很好的答案,我将提出 Divide and Conquer 方法,我不是为了消除其他好的答案而写这个答案,而是通过另一种可行的方法来实现它们对我有效。它有两个步骤splittingmerging熊猫数据框:

分而治之的优势:

  • 您无需使用向量化或任何其他方法即可将数据框的类型转换为另一种类型
  • 您不需要Cythonize代码,这通常需要花费额外的时间
  • 在我的情况下,iterrows()itertuples()在整个数据帧上的性能相同
  • 取决于对切片index的选择,您将能够以指数方式加快迭代速度。 index越高,迭代过程越快。

分而治之的缺点:

  • 您不应在迭代过程中依赖相同的数据框和不同的 slice 。这意味着,如果您想从其他 slice 进行读取或写入,则可能很难做到这一点。

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

步骤1:拆分/切片

在此步骤中,我们将在整个数据帧上划分迭代。认为您将要读取csv文件到pandas df中,然后对其进行遍历。在可能的情况下,我有5,000,000条记录,我打算将其拆分为100,000条记录。

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

Number of records | Iteration per second
========================================
100,000           | 500 it/s
500,000           | 200 it/s
1,000,000         | 50 it/s
5,000,000         | 20 it/s

第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')

参考:

Efficient way of iteration over datafreame

Concatenate csv files into one Pandas Dataframe

答案 19 :(得分:1)

此示例使用iloc隔离数据帧中的每个数字。

import pandas as pd

 a = [1, 2, 3, 4]
 b = [5, 6, 7, 8]

 mjr = pd.DataFrame({'a':a, 'b':b})

 size = mjr.shape

 for i in range(size[0]):
     for j in range(size[1]):
         print(mjr.iloc[i, j])

答案 20 :(得分:1)

有很多方法可以遍历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]])

答案 21 :(得分:1)

您还可以numpy建立索引,以获得更高的速度。它不是真正的迭代,但比某些应用程序的迭代效果要好得多。

subset = row['c1'][0:5]
all = row['c1'][:]

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

np.asarray(all)
imgs[:] = cv2.resize(imgs[:], (224,224) ) #resize every image in an hdf5 file

答案 22 :(得分:0)

只需加上我的两分钱,

正如公认的答案所述,对行应用函数的最快方法是使用向量化函数,即所谓的 numpy ufuncs(通用函数)

但是如果您要应用的功能尚未在 numpy 中实现,您应该怎么做?

使用 vectorize 中的 numba 装饰器,您可以像这样直接在 Python 中轻松创建 ufunc:

from numba import vectorize, float64

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

此函数的文档在这里:https://numba.pydata.org/numba-doc/latest/user/vectorize.html

答案 23 :(得分:0)

对于查看和修改值,我将使用iterrows()。在for循环中,通过使用元组拆包(请参见示例:i, row),我使用row仅查看值,并在情况下将iloc方法一起使用我想修改值。如先前的答案所述,您不应在此处修改要迭代的内容。

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

此处循环中的row是该行的副本,而不是它的视图。因此,您不应编写类似row['A'] = 'New_Value'的内容,它不会修改DataFrame。但是,您可以使用iloc并指定DataFrame来完成工作。

答案 24 :(得分:0)

使用df.iloc[]。例如,使用数据框“rows_df”:

Enter image description here

或者

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

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

Enter image description here

答案 25 :(得分:0)

正如这里的许多答案正确且清楚地指出的那样,您通常不应该尝试在 Pandas 中循环,而应该编写矢量化代码。但是问题仍然存在,你是否应该永远在 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

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

df.groupby( (df.y==0).cumsum() )['x'].cumsum()

这对于大多数情况来说已经足够快了,尽管您也可以通过避免使用 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() )

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

if y[i] > 0: x_sum[i] = x_sum[i-1] + x[i]

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

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

答案 26 :(得分:0)

某些库(例如,我使用的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慢​​很多,但是在某些情况下是必需的。

答案 27 :(得分:-1)

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

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

for c1, c2 in df.values:
    # ...

注意:

  • documentation 明确建议改用 .to_numpy()
  • 生成的 NumPy 数组将具有适合所有列的 dtype,在最坏的情况下 object
  • good reasons 首先不要使用循环

不过,我认为这个选项应该包括在这里,作为一个(应该认为)微不足道的问题的直接解决方案。

答案 28 :(得分:-2)

为什么使事情复杂化?

简单。

import pandas as pd
import numpy as np

# Here is an example dataframe
df_existing = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD'))

for idx,row in df_existing.iterrows():
    print row['A'],row['B'],row['C'],row['D']