如何在 Pandas 中遍历 DataFrame 中的行
Posted
技术标签:
【中文标题】如何在 Pandas 中遍历 DataFrame 中的行【英文标题】:How to iterate over rows in a DataFrame in Pandas 【发布时间】:2013-05-04 19:45:20 【问题描述】:我有一个来自 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']
在 Pandas 中可以做到这一点吗?
我发现了这个similar question。但它并没有给我我需要的答案。例如,这里建议使用:
for date, row in df.T.iteritems():
或
for row in df.iterrows():
但我不明白 row
对象是什么以及如何使用它。
【问题讨论】:
df.iteritems() 迭代列而不是行。因此,要使其遍历行,您必须转置(“T”),这意味着您将行和列相互更改(反映在对角线上)。因此,当您使用 df.T.iteritems() 时,您可以有效地在其行上迭代原始数据帧 与 cs95 所说的相反,想要迭代数据框是有充分理由的,因此新用户不应感到气馁。一个例子是,如果您想使用每一行的值作为输入来执行一些代码。此外,如果您的数据框相当小(例如,少于 1000 个项目),那么性能并不是真正的问题。 @cs95 在我看来,数据框是 Python 中的首选表格式。因此,无论何时您想要读取 csv,或者您有一个要操作其值的 dicts 列表,或者您想要执行简单的连接、groupby 或窗口操作,您都可以使用数据框,即使您的数据比较小。 @cs95 不,但这是对“完全使用 DataFrame”的回应。我的观点是,这就是为什么人们可能在数据框中拥有自己的数据的原因。如果你想例如为数据的每一行运行一个脚本,您必须遍历该数据框。 我第二个@oulenz。据我所知,pandas
是读取 csv 文件的首选,即使数据集很小。使用 API 操作数据更容易编程
【参考方案1】:
DataFrame.iterrows
是一个生成器,它同时产生索引和行(作为一个系列):
import pandas as pd
df = pd.DataFrame('c1': [10, 11, 12], 'c2': [100, 110, 120])
for index, row in df.iterrows():
print(row['c1'], row['c2'])
10 100
11 110
12 120
【讨论】:
注意:“因为 iterrows 为每一行返回一个系列,它不保留跨行的 dtypes。”此外,“你不应该修改你正在迭代的东西。”根据pandas 0.19.1 docs @viddik13 非常感谢。因此,我遇到了一种情况,其中像431341610650
这样的数值读取为4.31E+11
。有没有办法保留数据类型?
@AzizAlto 使用itertuples
,如下所述。另见pandas.pydata.org/pandas-docs/stable/generated/…
不要使用 iterrows。 Itertuples 更快并保留数据类型。 More info
来自the documentation:“遍历pandas 对象通常很慢。在许多情况下,不需要手动遍历行[...]”。您的答案是正确的(在问题的上下文中),但在任何地方都没有提到这一点,所以它不是一个很好的答案。【参考方案2】:
如何在 Pandas 中遍历 DataFrame 中的行?
回答:不要*!
Pandas 中的迭代是一种反模式,只有在用尽所有其他选项时才应该这样做。您不应该使用名称中包含“iter
”的任何函数超过几千行,否则您将不得不习惯大量等待。
你想打印一个 DataFrame 吗?使用DataFrame.to_string()
。
你想计算一些东西吗?在这种情况下,按此顺序搜索方法(列表修改自here):
-
矢量化
Cython套路
列表理解(vanilla
for
循环)
DataFrame.apply()
:i) 可以在 Cython 中执行的缩减,ii) Python 空间中的迭代
DataFrame.itertuples()
和 iteritems()
DataFrame.iterrows()
iterrows
和 itertuples
(在这个问题的答案中都获得了很多投票)应该在非常罕见的情况下使用,例如为顺序处理生成行对象/名称元组,这确实是这些函数唯一有用的地方为。
向当局申诉
The documentation page 在迭代中有一个巨大的红色警告框,上面写着:
遍历 pandas 对象通常很慢。在许多情况下,不需要手动迭代行 [...]。
* 它实际上比“不要”要复杂一些。 df.iterrows()
是这个问题的正确答案,但“矢量化你的操作”是更好的答案。我承认在某些情况下无法避免迭代(例如,某些操作的结果取决于为前一行计算的值)。但是,需要对库有一定的了解才能知道何时。如果您不确定是否需要迭代解决方案,您可能不需要。 PS:要了解更多关于我写这个答案的理由,请跳到最底部。
比循环更快:Vectorization, Cython
大量的基本操作和计算由 pandas“矢量化”(通过 NumPy 或通过 Cythonized 函数)。这包括算术、比较、(大多数)归约、重塑(例如旋转)、连接和 groupby 操作。查看Essential Basic Functionality 上的文档,为您的问题找到合适的矢量化方法。
如果不存在,请随意使用自定义Cython extensions 编写自己的。
下一个最好的东西:List Comprehensions*
如果 1) 没有可用的矢量化解决方案,2) 性能很重要,但还不足以解决对代码进行 cythonizing 的麻烦,以及 3) 您正在尝试对代码执行元素转换。有一个 good amount of evidence 表明列表解析对于许多常见的 Pandas 任务来说足够快(有时甚至更快)。
公式很简单,
# 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 - same data type
result = [f(row[0], ..., row[n]) for row in df[['col1', ...,'coln']].to_numpy()]
# Iterating over multiple columns - differing data type
result = [f(row[0], ..., row[n]) for row in zip(df['col1'], ..., df['coln'])]
如果您可以将业务逻辑封装到函数中,则可以使用调用它的列表推导。您可以通过原始 Python 代码的简单性和速度使任意复杂的事情工作。
注意事项
列表推导假定您的数据易于使用 - 这意味着您的数据类型是一致的并且您没有 NaN,但这并不总是得到保证。
-
第一个更明显,但是在处理 NaN 时,如果存在内置的 pandas 方法(因为它们具有更好的极端情况处理逻辑),则更喜欢它们,或者确保您的业务逻辑包含适当的 NaN 处理逻辑。
在处理混合数据类型时,您应该迭代
zip(df['A'], df['B'], ...)
而不是 df[['A', 'B']].to_numpy()
,因为后者隐式地将数据向上转换为最常见的类型。例如,如果 A 是数字,B 是字符串,to_numpy()
会将整个数组转换为字符串,这可能不是您想要的。幸运的是,zip
ping 你的列是最直接的解决方法。
*您的里程可能会因上述注意事项部分中所述的原因而有所不同。
一个明显的例子
让我们通过添加两个 pandas 列 A + B
的简单示例来演示区别。这是一个可向量化的操作,因此很容易对比上述方法的性能。
Benchmarking code, for your reference。底部的行测量了一个用 numpandas 编写的函数,这是一种与 NumPy 大量混合以挤出最大性能的 Pandas 风格。除非您知道自己在做什么,否则应避免编写 numpandas 代码。尽可能坚持使用 API(即,更喜欢 vec
而不是 vec_numpy
)。
不过,我应该提一下,它并不总是这么干脆利落的。有时,“什么是最佳操作方法”的答案是“这取决于您的数据”。我的建议是在确定一种方法之前先对您的数据测试不同的方法。
我的个人意见*
对 iter 系列的各种替代品进行的大多数分析都是从性能角度进行的。但是,在大多数情况下,您通常会处理大小合理的数据集(不超过几千或 100K 行),而性能将仅次于解决方案的简单性/可读性。
这是我在选择解决问题的方法时的个人偏好。
对于新手:
矢量化(如果可能);
apply()
;列出理解;itertuples()
/iteritems()
;iterrows()
;赛通
对于更有经验的人:
矢量化(如果可能);
apply()
;列出理解;赛通;itertuples()
/iteritems()
;iterrows()
对于任何可以向量化的问题,向量化是最惯用的方法。始终寻求矢量化!如有疑问,请查阅文档,或在 Stack Overflow 上查看有关您特定任务的现有问题。
我确实倾向于在我的很多帖子中继续谈论apply
的糟糕程度,但我承认初学者更容易理解它正在做什么。此外,apply
有很多用例,在this post of mine 中有说明。
Cython 在列表中排名较低,因为它需要更多的时间和精力才能正确完成。通常,您永远不需要使用 pandas 编写需要这种性能水平的代码,即使是列表解析也无法满足。
* 与任何个人意见一样,请多加注意!
进一步阅读
10 Minutes to pandas 和 Essential Basic Functionality - 向您介绍 Pandas 及其矢量化*/cythonized 函数库的有用链接。
Enhancing Performance - 增强标准 Pandas 操作的文档入门
Are for-loops in pandas really bad? When should I care? - 我对列表推导及其对各种操作(主要是涉及非数字数据的操作)的适用性的详细说明
When should I (not) want to use pandas apply() in my code? - apply
很慢(但没有iter*
家族那么慢。但是,在某些情况下可以(或应该)将apply
视为一种严重的替代方案,尤其是在某些GroupBy
操作中)。
* Pandas 字符串方法是“矢量化的”,因为它们是在系列上指定的,但对每个元素都进行操作。底层机制仍然是迭代的,因为字符串操作本质上很难向量化。
我为什么写这个答案
我注意到新用户的一个常见趋势是提出“如何迭代我的 df 以执行 X?”形式的问题。显示在 for
循环内执行某些操作时调用 iterrows()
的代码。这就是为什么。一个没有被引入向量化概念的库的新用户可能会将解决他们问题的代码设想为迭代他们的数据来做某事。不知道如何迭代 DataFrame,他们做的第一件事就是用谷歌搜索它,然后在这个问题上结束。然后,他们看到接受的答案告诉他们如何去做,然后他们闭上眼睛运行这段代码,而不会首先质疑迭代是否是正确的做法。
此答案的目的是帮助新用户了解迭代不一定是所有问题的解决方案,并且可能存在更好、更快和更惯用的解决方案,值得花时间去探索它们。我并不是想发起一场迭代与矢量化的战争,但我希望新用户在使用这个库开发解决他们的问题的解决方案时得到通知。
【讨论】:
请注意,iterrows
和 itertuples
有一些重要的注意事项。请参阅this answer 和pandas docs 了解更多详情。
这是唯一一个专注于 Pandas 应该使用的惯用技术的答案,使其成为该问题的最佳答案。学习使用 right 代码(而不是right)获得正确 答案回答 错误 代码 - 即效率低下,无法扩展,太适合特定数据)是学习熊猫的重要组成部分(和一般数据)。
我认为你对 for 循环不公平,因为在我的测试中它们只比列表理解慢一点。诀窍是循环 zip(df['A'], df['B'])
而不是 df.iterrows()
。
在 List Comprehensions 下,“迭代多列”示例需要注意:DataFrame.values
会将每一列转换为通用数据类型。 DataFrame.to_numpy()
也这样做。幸运的是,我们可以将zip
用于任意数量的列。
@Dean 我经常收到这个回复,老实说让我很困惑。这一切都是为了养成良好的习惯。 “我的数据很小,性能并不重要,所以我可以原谅我使用这种反模式”..?当有一天性能确实很重要时,您会感谢自己提前准备了正确的工具。【参考方案3】:
首先考虑您是否真的需要迭代 DataFrame 中的行。请参阅this answer 了解替代方案。
如果您仍需要遍历行,可以使用以下方法。请注意一些其他答案中未提及的重要警告。
DataFrame.iterrows()
for index, row in df.iterrows():
print(row["c1"], row["c2"])
DataFrame.itertuples()
for row in df.itertuples(index=True, name='Pandas'):
print(row.c1, row.c2)
itertuples()
应该比iterrows()
快
但请注意,根据文档(目前为 pandas 0.24.2):
iterrows:dtype
可能无法逐行匹配
iterrows:不要修改行因为 iterrows 为每一行返回一个系列,它不会跨行保留 dtypes(dtypes 在DataFrames 的列中保留)。要在遍历行时保留 dtypes,最好使用 itertuples(),它返回值的命名元组,通常比 iterrows() 快得多
你应该永远不要修改你正在迭代的东西。这不能保证在所有情况下都有效。根据数据类型,迭代器返回一个副本而不是一个视图,写入它不会有任何效果。
改用DataFrame.apply():
new_df = df.apply(lambda x: x * 2)
迭代器:
如果列名是无效的 Python 标识符、重复或以下划线开头,它们将被重命名为位置名称。对于大量列 (>255),将返回常规元组。
更多详情请见pandas docs on iteration。
【讨论】:
只是一个小问题,有人在完成后很长时间阅读此线程:df.apply() 在效率方面与 itertuples 相比如何? 注意:你也可以说类似for row in df[['c1','c2']].itertuples(index=True, name=None):
来在行迭代器中只包含某些列。
你可以只使用getattr(row, "c1")
,而不是row.c1
。
我有大约 90% 的把握,如果您使用 getattr(row, "c1")
而不是 row.c1
,您将失去 itertuples
的任何性能优势,并且如果您确实需要通过字符串访问该属性, 你应该改用 iterrows。
我偶然发现了这个问题,因为虽然我知道有拆分应用组合,但我仍然确实需要迭代 DataFrame(如问题所述)。不是每个人都可以使用numba
和cython
进行改进(相同的文档说“首先在 Python 中进行优化总是值得的”)。我写这个答案是为了帮助其他人避免(有时令人沮丧)的问题,因为其他答案都没有提到这些警告。误导任何人或告诉“这是正确的做法”从来都不是我的本意。我已经改进了答案。【参考方案4】:
您应该使用df.iterrows()
。虽然逐行迭代并不是特别有效,因为必须创建 Series
对象。
【讨论】:
这是否比将 DataFrame 转换为 numpy 数组(通过 .values)并直接对数组进行操作更快?我有同样的问题,但最终转换为 numpy 数组,然后使用 cython。 @vgoklani 如果逐行迭代效率低下并且您有一个非对象 numpy 数组,那么几乎可以肯定使用原始 numpy 数组会更快,尤其是对于具有多行的数组。你应该避免遍历行,除非你必须这样做 我对 df.iterrows()、df.itertuples() 和 zip(df['a'], df['b']) 的时间消耗做了一些测试将结果发布在另一个问题的答案中:***.com/a/34311080/2142098【参考方案5】:虽然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
【讨论】:
您的两个示例中的大部分时间差异似乎是由于您似乎对 .iterrows() 命令使用基于标签的索引,而对 .iterrows() 命令使用基于整数的索引。 itertuples() 命令。 对于基于财务数据的数据帧(时间戳和 4 倍浮点数),itertuples 比我的机器上的 iterrows 快 19,57 倍。只有for a,b,c in izip(df["a"],df["b"],df["c"]:
几乎同样快。
你能解释一下为什么它更快吗?
@AbeMiessler iterrows()
将每行数据打包成一个系列,而itertuples()
没有。
请注意,列的顺序实际上是不确定的,因为df
是从字典创建的,所以row[1]
可以引用任何列。事实证明,尽管整数与浮点列的时间大致相同。【参考方案6】:
您还可以使用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['price'] 是指数据框中的列名吗?我正在尝试创建一个字典,其中包含 csv 文件中多个列的唯一值。我用你的逻辑创建了一个具有唯一键和值的字典,并得到一个错误说明 TypeError: ("'Series' objects are mutable, 因此它们不能被散列", u'occurred at index 0') 代码: df['Workclass'] = df.apply(lambda row: dic_update(row), axis=1) 行尾 id = 0 行尾 def dic_update(row): 如果行不在 dic: dic[row] = id id = id + 1 轴默认为0是最糟糕的 请注意,apply
不会“迭代”行,而是逐行应用函数。如果您确实确实需要迭代和自下而上,则上述代码将不起作用,例如在比较不同行的值时(在这种情况下,您只能进行迭代)。
这是熊猫的合适答案【参考方案7】:
您可以按如下方式使用 df.iloc 函数:
for i in range(0, len(df)):
print df.iloc[i]['c1'], df.iloc[i]['c2']
【讨论】:
我知道应该避免这种情况而使用 iterrows 或 itertuples,但知道为什么会很有趣。有什么想法吗? 如果您想保留数据类型并按名称引用列,这是我所知道的唯一有效技术。itertuples
保留数据类型,但去掉它不喜欢的任何名称。 iterrows
则相反。
花了好几个小时试图通过 pandas 数据结构的特性来做一些简单而富有表现力的事情。这会产生可读的代码。
虽然for i in range(df.shape[0])
可能会加快这种方法的速度,但对于我的应用程序,它仍然比上面的 iterrows() 方法慢 3.5 倍。
在大型 Datafrmes 上这似乎更好,因为 my_iter = df.itertuples()
需要双倍的内存和大量时间来复制它。 iterrows()
也一样。【参考方案8】:
如何高效迭代
如果您确实需要迭代 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))
2) 默认的itertuples()
已经快很多了,但它不适用于My Col-Name is very Strange
等列名(如果您的列重复或列名不能简单地转换为Python 变量名)。:
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()
使用 name=None 更快,但不是很方便,因为您必须为每列定义一个变量。
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))
4) 最后,命名为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
This article is a very interesting comparison between iterrows and itertuples
【讨论】:
【参考方案9】:我一直在寻找如何迭代行 和 列并在这里结束:
for i, row in df.iterrows():
for j, column in row.iteritems():
print(column)
【讨论】:
尽可能避免使用 iterrows()。我在答案中解释了原因How to iterate efficiently【参考方案10】:您可以编写自己的迭代器来实现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);
【讨论】:
对于不想阅读代码的人:蓝线是intertuples
,橙线是通过yield块的迭代器列表。 interrows
未进行比较。【参考方案11】:
要循环 dataframe
中的所有行,您可以使用:
for x in range(len(date_example.index)):
print date_example['Date'].iloc[x]
【讨论】:
这是链式索引。我不建议这样做。 @cs95 你会推荐什么?【参考方案12】: for ind in df.index:
print df['c1'][ind], df['c2'][ind]
【讨论】:
在大型数据帧(例如数百万行)上使用此选项时的性能如何? 老实说,我也不清楚,我认为与最佳答案相比,经过的时间大致相同,因为两种情况都使用“for”构造。但在某些情况下内存可能会有所不同。 这是链式索引。不要使用这个!【参考方案13】:有时有用的模式是:
# 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
【讨论】:
【参考方案14】:总之
尽可能使用矢量化 如果操作无法向量化 - 使用列表推导 如果您需要代表整行的单个对象 - 使用 itertuples 如果上面的速度太慢 - 试试 swifter.apply 如果还是太慢 - 试试Cython 例程基准测试
【讨论】:
【参考方案15】:更新:cs95 已更新 his answer 以包含普通的 numpy 矢量化。你可以参考他的回答。
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]
【讨论】:
你是怎么画这个的? cs95's benchmarking code, for your reference【参考方案16】:要循环dataframe
中的所有行并使用每一行的值方便,namedtuples
可以转换为ndarray
s。例如:
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]
请注意,如果index=True
,索引将作为元组的第一个元素添加,这对于某些应用程序可能是不可取的。
【讨论】:
【参考方案17】:有一种方法可以在获取 DataFrame 而不是 Series 的同时迭代 throw 行。我没有看到有人提到您可以将索引作为列表传递给要作为 DataFrame 返回的行:
for i in range(len(df)):
row = df.iloc[[i]]
注意双括号的使用。这将返回一个包含单行的 DataFrame。
【讨论】:
这对于在排序后获取数据框中的第 n 大行非常有帮助。谢谢!【参考方案18】:对于查看和修改值,我会使用iterrows()
。在 for 循环中并通过使用元组解包(参见示例:i, row
),我使用 row
仅查看值并在我想修改值时使用 i
和 loc
方法。如之前的答案所述,在这里您不应该修改您正在迭代的内容。
for i, row in df.iterrows():
df_column_A = df.loc[i, 'A']
if df_column_A == 'Old_Value':
df_column_A = 'New_value'
这里循环中的row
是该行的副本,而不是它的视图。因此,你不应该写像row['A'] = 'New_Value'
这样的东西,它不会修改DataFrame。但是,您可以使用i
和loc
并指定DataFrame 来完成这项工作。
【讨论】:
【参考方案19】:我们有多种选择来做同样的事情,很多人都分享了他们的答案。
我发现以下两种方法既简单又高效:
-
DataFrame.iterrows()
DataFrame.itertuples()
例子:
import pandas as pd
inp = ['c1':10, 'c2':100, 'c1':11,'c2':110, 'c1':12,'c2':120]
df = pd.DataFrame(inp)
print (df)
#With iterrows method
for index, row in df.iterrows():
print(row["c1"], row["c2"])
#With itertuples method
for row in df.itertuples(index=True, name='Pandas'):
print(row.c1, row.c2)
注意:itertuples() 应该比 iterrows() 快
【讨论】:
这实际上回答了这个问题。 +1【参考方案20】:有很多方法可以遍历 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】:最简单的方法,使用apply
函数
def print_row(row):
print row['c1'], row['c2']
df.apply(lambda row: print_row(row), axis=1)
【讨论】:
【参考方案22】:正如这里的许多答案正确而清楚地指出的那样,您通常不应该尝试在 Pandas 中循环,而应该编写矢量化代码。但是问题仍然存在,您是否应该曾经在 Pandas 中编写循环,如果是,那么在这些情况下循环的最佳方式。
我相信至少在一种一般情况下循环是合适的:当您需要以某种复杂的方式计算一些依赖于 other 行中的值的函数时。在这种情况下,循环代码通常比矢量化代码更简单、更易读且不易出错。 循环代码甚至可能更快。
我将尝试用一个例子来说明这一点。假设您想要获取一列的累积总和,但每当其他列等于 0 时将其重置:
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 一无所知,也可以阅读:
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 循环方法的可读性或速度可能是有意义的。
【讨论】:
【参考方案23】:您还可以进行 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
【讨论】:
【参考方案24】:此示例使用 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])
【讨论】:
【参考方案25】:某些库(例如,我使用的 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 慢很多,但在某些情况下是必要的。
【讨论】:
【参考方案26】:df.iterrows() 返回元组(a,b),其中 a 是索引,b 是行。
【讨论】:
【参考方案27】:除了这篇文章中的出色答案之外,我还将提出 分而治之 方法,我写这个答案并不是为了废除其他出色的答案,而是用另一种可行的方法来实现它们对我有效。它有两个步骤splitting
和merging
pandas 数据框:
分而治之的优点:
您无需使用矢量化或任何其他方法将数据帧的类型转换为另一种类型 您无需对代码进行 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
【讨论】:
【参考方案28】:只需加上我的两分钱,
正如公认的答案所述,将函数应用于行的最快方法是使用矢量化函数,即所谓的 numpy ufuncs
(通用函数)
但是当你要应用的功能在numpy
中还没有实现的时候怎么办呢?
使用numba
中的vectorize
装饰器,您可以像这样直接在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
【讨论】:
【参考方案29】:可能是最优雅的解决方案(但肯定不是最有效的):
for row in df.values:
c2 = row[1]
print(row)
# ...
for c1, c2 in df.values:
# ...
请注意:
documentation 明确建议改用.to_numpy()
生成的 NumPy 数组将具有适合所有列的 dtype,在最坏的情况下 object
有good reasons首先不要使用循环
不过,我认为这个选项应该包括在这里,作为一个(人们应该认为的)琐碎问题的直接解决方案。
【讨论】:
【参考方案30】:更好的方法是使用 zip 创建键值对将数据帧转换为字典,然后通过键访问行值。我的回答显示了如何使用字典作为熊猫的替代品。有些人认为字典和元组更有效。您可以轻松地将字典替换为命名元组列表。
inp = ['c1':10, 'c2':100, 'c1':11,'c2':110, 'c1':12,'c2':120]
df = pd.DataFrame(inp)
print(df)
for row in inp:
for (k,v) in zip(row.keys(), row.values()):
print(k,v)
输出:
c1 10
c2 100
c1 11
c2 110
c1 12
c2 120
【讨论】:
以上是关于如何在 Pandas 中遍历 DataFrame 中的行的主要内容,如果未能解决你的问题,请参考以下文章
遍历 Pandas Dataframe 中定义的日期时间索引范围
pandas.DataFrame.loc好慢,怎么遍历访问DataFrame比较快