我有一个熊猫数据帧,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对象是什么,以及如何使用它。


当前回答

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

其他回答

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

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

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

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

免责声明:尽管这里有很多答案建议不要使用迭代(循环)方法(我基本同意),但我仍然认为这是一种适用于以下情况的合理方法:

使用API中的数据扩展数据帧

假设您有一个包含不完整用户数据的大型数据帧。现在,您必须使用其他列来扩展此数据,例如,用户的年龄和性别。

这两个值都必须从后端API获取。我假设API不提供“批处理”端点(一次接受多个用户ID)。否则,您应该只调用一次API。

网络请求的成本(等待时间)远远超过了数据帧的迭代。我们讨论的是数百毫秒的网络往返时间,相比之下,使用迭代的替代方法可以忽略不计的小增益。

每行一个昂贵的网络请求

所以在这种情况下,我绝对倾向于使用迭代方法。尽管网络请求很昂贵,但可以保证对数据帧中的每一行只触发一次。以下是使用DataFrame.iterrows的示例:

实例

for index, row in users_df.iterrows():
  user_id = row['user_id']

  # Trigger expensive network request once for each row
  response_dict = backend_api.get(f'/api/user-data/{user_id}')

  # Extend dataframe with multiple data from response
  users_df.at[index, 'age'] = response_dict.get('age')
  users_df.at[index, 'gender'] = response_dict.get('gender')

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

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

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

注意:

文档明确建议改用.to_numpy()在最坏的情况下,生成的NumPy数组将具有适合所有列的dtype对象首先有充分的理由不使用循环

尽管如此,我认为这个选项应该包含在这里,作为一个(人们应该认为)微不足道的问题的直接解决方案。

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