Python Pandas如何迭代行并访问列名

68

我正在尝试迭代Python Pandas数据框的行。在数据框的每一行中,我试图通过其列名引用每个值。

这是我的代码:

import numpy as np
import pandas as pd

df = pd.DataFrame(np.random.rand(10,4),columns=list('ABCD'))
print df
          A         B         C         D
0  0.351741  0.186022  0.238705  0.081457
1  0.950817  0.665594  0.671151  0.730102
2  0.727996  0.442725  0.658816  0.003515
3  0.155604  0.567044  0.943466  0.666576
4  0.056922  0.751562  0.135624  0.597252
5  0.577770  0.995546  0.984923  0.123392
6  0.121061  0.490894  0.134702  0.358296
7  0.895856  0.617628  0.722529  0.794110
8  0.611006  0.328815  0.395859  0.507364
9  0.616169  0.527488  0.186614  0.278792

我使用这种方法进行迭代,但它只给我部分解决方案 - 在每次迭代中选择一行后,如何通过列名访问行元素?

这是我想要做的:

for row in df.iterrows():
    print row.loc[0,'A']
    print row.A
    print row.index()

我的理解是这一行是Pandas的Series。但我无法索引到该系列。

在同时迭代行的情况下是否可以使用列名?


4
在你的例子中,row 不是一个 Series,而应该是一个元组。但是如果你使用 for idx, row in df.iterrows()row['A'] 应该能正常工作。 - ayhan
那就是我所缺少的!谢谢。 - edesz
1
大多数使用 pandas 进行的数字操作都可以进行矢量化 - 这意味着它们比传统迭代要快得多。然而,一些操作(如字符串和正则表达式)本质上很难向量化。在这种情况下,重要的是要了解如何循环遍历您的数据。有关何时以及如何循环遍历数据的更多信息,请阅读使用 Pandas 进行 For 循环 - 何时应关注? - cs95
5个回答

95

我也喜欢 itertuples()

for row in df.itertuples():
    print(row.A)
    print(row.Index)

因为row是一个命名元组,如果你想访问每一行的值,这样做应该会快得多

速度运行:

df = pd.DataFrame([x for x in range(1000*1000)], columns=['A'])
st=time.time()
for index, row in df.iterrows():
    row.A
print(time.time()-st)
45.05799984931946

st=time.time()
for row in df.itertuples():
    row.A
print(time.time() - st)
0.48400020599365234

谢谢!我认为这实际上是我想要的(但是我记不起来了)。它更加实用(因为没有需要使用idx,例如枚举列表)。既然我要求使用iterrows(),那么我会采用那个答案。但如果我记得的话,这就是我会使用的方法。 - edesz
2
print(row.Index) results in: AttributeError: 'tuple' object has no attribute 'Index' - kiltek
@kiltek 你使用了 itertuples(index=False) 吗?如果没有,我需要一些代码来找出问题所在。 - Steven G
2
@WR 这应该是被接受的答案。它比原来快了50倍(https://github.com/mm-mansour/Fast-Pandas)。 - bbennett36
据我所知,如果列数大于255,则返回的元组将没有名称。有没有办法覆盖这个限制,为大约3000列生成命名元组?我最终想获取那些符合条件的列名。@Steven G - abbas786
很遗憾,255列的限制是namedtuple的限制,而不是pandas的限制。 - Steven G

35
< p >从iterrows()返回的项目不是Series,而是(index, Series)元组,因此您可以在for循环中解包元组,如下所示:

for (idx, row) in df.iterrows():
    print(row.loc['A'])
    print(row.A)
    print(row.index)

#0.890618586836
#0.890618586836
#Index(['A', 'B', 'C', 'D'], dtype='object')

@StevenG 是的,那就是我想说的。如果我们说(索引,系列),可能会更清楚。 - Psidom
使用第二个答案中建议的 itertuples()...如果你正在处理一个大的数据框,itertuples 会更快。 - Megha

32

如何有效地迭代

如果你真的需要迭代Pandas数据框,你可能会想要避免使用iterrows()。有不同的方法,通常的方法iterrows()远非最佳选择。使用itertuples()可快100倍。

简而言之:

  • 一般情况下,请使用df.itertuples(name=None)。特别是当您有固定数量的列且少于255列时。 见第(3)点
  • 否则,请使用df.itertuples(),除非您的列具有特殊字符,例如空格或'-'。 见第(2)点
  • 即使您的数据框具有奇怪的列名,也可以使用最后一个示例中的itertuples()见第(4)点
  • 只有在无法使用前面的解决方案时才使用iterrows()见第(1)点

在Pandas数据框中迭代行的不同方法:

生成一个有一百万行和四列的随机数据框:

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))
  • 默认的itertuples()已经非常快,但是如果你的列名重复或者一个列名不能简单地转换为Python变量名,那么它将无法使用。例如My Col-Name is very Strange,应该避免使用此方法。

  • 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))
    
  • 使用名为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("3. Itertuples done in {} seconds, result = {}".format(total_elapsed_time, result))
    
  • 最后,所命名的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("4. 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. Itertuples done in 0.94 seconds, result = 66151519
    4. Polyvalent Itertuples working even with special characters in the column name done in 2.94 seconds, result = 66151519
    

    这篇文章是关于iterrows和itertuples之间非常有趣的比较


    3
    这应该被接受为答案。 - Abhijat Biswas

    2

    这并不像我希望的那样直接。你需要使用enumerate来跟踪你有多少列。然后使用该计数器查找列的名称。被接受的答案没有向你展示如何动态访问列名。

    for row in df.itertuples(index=False, name=None):
        for k,v in enumerate(row):
            print("column: {0}".format(df.columns.values[k]))
            print("value: {0}".format(v)
    

    -1
    for i in range(1,len(na_rm.columns)):
               print ("column name:", na_rm.columns[i])
    

    输出:

    column name: seretide_price
    column name: symbicort_mkt_shr
    column name: symbicort_price
    

    这是低效的编写方式,for循环可以直接在列列表上进行迭代。 - CharlesG

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