Kie*_*nPC 82 python iteration performance pandas
我注意到从熊猫使用iterrows时性能非常差.
这是其他人经历过的事情吗?它是否特定于iterrows,并且对于特定大小的数据(我正在使用2-3百万行),是否应该避免此功能?
关于GitHub的讨论使我相信它是在数据帧中混合dtypes时引起的,但是下面的简单示例表明它甚至在使用一个dtype(float64)时也存在.我的机器需要36秒:
import pandas as pd
import numpy as np
import time
s1 = np.random.randn(2000000)
s2 = np.random.randn(2000000)
dfa = pd.DataFrame({'s1': s1, 's2': s2})
start = time.time()
i=0
for rowindex, row in dfa.iterrows():
i+=1
end = time.time()
print end - start
Run Code Online (Sandbox Code Playgroud)
为什么矢量化操作如此快速应用?我想也必须有一些逐行迭代.
在我的情况下,我无法弄清楚如何不使用iterrows(这将为将来的问题保存).因此,如果您一直能够避免这种迭代,我将不胜感激.我正在基于单独数据帧中的数据进行计算.谢谢!
---编辑:我想要运行的简化版本已添加到下面---
import pandas as pd
import numpy as np
#%% Create the original tables
t1 = {'letter':['a','b'],
'number1':[50,-10]}
t2 = {'letter':['a','a','b','b'],
'number2':[0.2,0.5,0.1,0.4]}
table1 = pd.DataFrame(t1)
table2 = pd.DataFrame(t2)
#%% Create the body of the new table
table3 = pd.DataFrame(np.nan, columns=['letter','number2'], index=[0])
#%% Iterate through filtering relevant data, optimizing, returning info
for row_index, row in table1.iterrows():
t2info = table2[table2.letter == row['letter']].reset_index()
table3.ix[row_index,] = optimize(t2info,row['number1'])
#%% Define optimization
def optimize(t2info, t1info):
calculation = []
for index, r in t2info.iterrows():
calculation.append(r['number2']*t1info)
maxrow = calculation.index(max(calculation))
return t2info.ix[maxrow]
Run Code Online (Sandbox Code Playgroud)
Jef*_*eff 153
通常,iterrows
只应在非常特殊的情况下使用.这是执行各种操作的一般优先顺序:
1) vectorization
2) using a custom cython routine
3) apply
a) reductions that can be performed in cython
b) iteration in python space
4) itertuples
5) iterrows
6) updating an empty frame (e.g. using loc one-row-at-a-time)
Run Code Online (Sandbox Code Playgroud)
使用自定义的cython例程通常过于复杂,所以现在让我们跳过它.
1)矢量化始终是第一个也是最好的选择.然而,有一小部分案例无法以明显的方式进行矢量化(主要涉及复发).此外,在较小的框架上,执行其他方法可能更快.
3)应用包括可通常是通过在用Cython空间迭代器(这在大熊猫内部完成的)来进行(这是一个)的情况下.
这取决于apply表达式内部的内容.例如,df.apply(lambda x: np.sum(x))
将很快执行(当然df.sum(1)
更好).但是类似于:df.apply(lambda x: x['b'] + 1)
将在python空间中执行,因此速度较慢.
4)itertuples
不将数据打包成一个系列,只是将其作为元组返回
5)iterrows
将数据打包成系列.除非你真的需要这个,否则使用另一种方法.
6)一次更新空帧a-single-row.我见过这种方法过于使用了WAY.它是迄今为止最慢的.它可能是常见的(并且对于某些python结构来说相当快),但是DataFrame对索引进行了相当多的检查,因此一次更新行总是非常慢.更好地创造新的结构和concat
.
chr*_*ock 16
Numpy和pandas中的向量操作比vanilla Python中的标量操作快得多,原因如下:
摊销类型查找
Python是一种动态类型语言,因此数组中的每个元素都有运行时开销.但是,Numpy(以及大熊猫)在C中执行计算(通常通过Cython).数组的类型仅在迭代开始时确定; 仅此节省就是最大的胜利之一.
更好的缓存
迭代C数组是缓存友好的,因此速度非常快.pandas DataFrame是一个"面向列的表",这意味着每列实际上只是一个数组.因此,您可以对DataFrame执行的本机操作(如汇总列中的所有元素)将几乎没有缓存未命中.
更多并行机会
可以通过SIMD指令操作简单的C阵列.Numpy的某些部分启用SIMD,具体取决于您的CPU和安装过程.并行性的好处不会像静态类型和更好的缓存那样引人注目,但它们仍然是一个坚实的胜利.
故事的道德:使用Numpy和pandas中的矢量操作.它们比Python中的标量操作更快,原因很简单,这些操作正是C程序员手工编写的.(除了数组概念比带有嵌入式SIMD指令的显式循环更容易阅读.)
cs9*_*s95 13
...或者iteritems
,或者itertuples
。说真的,不要。只要有可能,就寻求对代码进行矢量化。如果你不相信我,就去问杰夫。
我承认,迭代DataFrame 有合法的用例,但有比iter*
族函数更好的迭代替代方案,即
通常有太多的 pandas 初学者会提出涉及与 相关的代码的问题iterrows
。由于这些新用户可能不熟悉矢量化的概念,因此他们将解决问题的代码设想为涉及循环或其他迭代例程的代码。也不知道如何迭代,他们通常会遇到这个问题并学到所有错误的东西。
迭代 pandas 对象通常很慢。在许多情况下,不需要手动迭代行[...]。
如果这还不能说服您,请看一下添加两列“A + B”的矢量化和非矢量化技术之间的性能比较,摘自我的 帖子。
基准测试代码,供大家参考。iterrows
是迄今为止最糟糕的方法,还值得指出的是,其他迭代方法也好不了多少。
底部的行测量了用 numpandas 编写的函数,numpandas 是一种与 NumPy 大量混合以发挥最大性能的 Pandas 风格。除非您知道自己在做什么,否则应避免编写 numpandas 代码。尽可能坚持使用 API(即,vec
更喜欢使用 API vec_numpy
)。
始终寻求矢量化。有时,根据问题或数据的性质,这并不总是可能的,因此寻求比iterrows
. 除了处理极少数行时的便利性之外,几乎没有合法的用例,否则,当您的代码可能运行数小时时,请准备好等待大量的时间。
查看下面的链接以确定解决您的代码的最佳方法/向量化例程。
10 分钟了解 pandas和基本基本功能- 向您介绍 Pandas 及其矢量化*/cythonized 函数库的有用链接。
增强性能- 有关增强标准 Pandas 操作的文档入门
这是解决问题的方法.这都是矢量化的.
In [58]: df = table1.merge(table2,on='letter')
In [59]: df['calc'] = df['number1']*df['number2']
In [60]: df
Out[60]:
letter number1 number2 calc
0 a 50 0.2 10
1 a 50 0.5 25
2 b -10 0.1 -1
3 b -10 0.4 -4
In [61]: df.groupby('letter')['calc'].max()
Out[61]:
letter
a 25
b -1
Name: calc, dtype: float64
In [62]: df.groupby('letter')['calc'].idxmax()
Out[62]:
letter
a 1
b 2
Name: calc, dtype: int64
In [63]: df.loc[df.groupby('letter')['calc'].idxmax()]
Out[63]:
letter number1 number2 calc
1 a 50 0.5 25
2 b -10 0.1 -1
Run Code Online (Sandbox Code Playgroud)
另一种选择是使用to_records()
,这是比速度更快itertuples
和iterrows
.
但对于您的情况,其他类型的改进还有很大的空间.
这是我的最终优化版本
def iterthrough():
ret = []
grouped = table2.groupby('letter', sort=False)
t2info = table2.to_records()
for index, letter, n1 in table1.to_records():
t2 = t2info[grouped.groups[letter].values]
# np.multiply is in general faster than "x * y"
maxrow = np.multiply(t2.number2, n1).argmax()
# `[1:]` removes the index column
ret.append(t2[maxrow].tolist()[1:])
global table3
table3 = pd.DataFrame(ret, columns=('letter', 'number2'))
Run Code Online (Sandbox Code Playgroud)
基准测试:
-- iterrows() --
100 loops, best of 3: 12.7 ms per loop
letter number2
0 a 0.5
1 b 0.1
2 c 5.0
3 d 4.0
-- itertuple() --
100 loops, best of 3: 12.3 ms per loop
-- to_records() --
100 loops, best of 3: 7.29 ms per loop
-- Use group by --
100 loops, best of 3: 4.07 ms per loop
letter number2
1 a 0.5
2 b 0.1
4 c 5.0
5 d 4.0
-- Avoid multiplication --
1000 loops, best of 3: 1.39 ms per loop
letter number2
0 a 0.5
1 b 0.1
2 c 5.0
3 d 4.0
Run Code Online (Sandbox Code Playgroud)
完整代码:
import pandas as pd
import numpy as np
#%% Create the original tables
t1 = {'letter':['a','b','c','d'],
'number1':[50,-10,.5,3]}
t2 = {'letter':['a','a','b','b','c','d','c'],
'number2':[0.2,0.5,0.1,0.4,5,4,1]}
table1 = pd.DataFrame(t1)
table2 = pd.DataFrame(t2)
#%% Create the body of the new table
table3 = pd.DataFrame(np.nan, columns=['letter','number2'], index=table1.index)
print('\n-- iterrows() --')
def optimize(t2info, t1info):
calculation = []
for index, r in t2info.iterrows():
calculation.append(r['number2'] * t1info)
maxrow_in_t2 = calculation.index(max(calculation))
return t2info.loc[maxrow_in_t2]
#%% Iterate through filtering relevant data, optimizing, returning info
def iterthrough():
for row_index, row in table1.iterrows():
t2info = table2[table2.letter == row['letter']].reset_index()
table3.iloc[row_index,:] = optimize(t2info, row['number1'])
%timeit iterthrough()
print(table3)
print('\n-- itertuple() --')
def optimize(t2info, n1):
calculation = []
for index, letter, n2 in t2info.itertuples():
calculation.append(n2 * n1)
maxrow = calculation.index(max(calculation))
return t2info.iloc[maxrow]
def iterthrough():
for row_index, letter, n1 in table1.itertuples():
t2info = table2[table2.letter == letter]
table3.iloc[row_index,:] = optimize(t2info, n1)
%timeit iterthrough()
print('\n-- to_records() --')
def optimize(t2info, n1):
calculation = []
for index, letter, n2 in t2info.to_records():
calculation.append(n2 * n1)
maxrow = calculation.index(max(calculation))
return t2info.iloc[maxrow]
def iterthrough():
for row_index, letter, n1 in table1.to_records():
t2info = table2[table2.letter == letter]
table3.iloc[row_index,:] = optimize(t2info, n1)
%timeit iterthrough()
print('\n-- Use group by --')
def iterthrough():
ret = []
grouped = table2.groupby('letter', sort=False)
for index, letter, n1 in table1.to_records():
t2 = table2.iloc[grouped.groups[letter]]
calculation = t2.number2 * n1
maxrow = calculation.argsort().iloc[-1]
ret.append(t2.iloc[maxrow])
global table3
table3 = pd.DataFrame(ret)
%timeit iterthrough()
print(table3)
print('\n-- Even Faster --')
def iterthrough():
ret = []
grouped = table2.groupby('letter', sort=False)
t2info = table2.to_records()
for index, letter, n1 in table1.to_records():
t2 = t2info[grouped.groups[letter].values]
maxrow = np.multiply(t2.number2, n1).argmax()
# `[1:]` removes the index column
ret.append(t2[maxrow].tolist()[1:])
global table3
table3 = pd.DataFrame(ret, columns=('letter', 'number2'))
%timeit iterthrough()
print(table3)
Run Code Online (Sandbox Code Playgroud)
最终版本比原始代码快近10倍.策略是:
groupby
以避免重复的比较值的.to_records
访问原始numpy.records对象. 归档时间: |
|
查看次数: |
34751 次 |
最近记录: |