我正在努力加快几个大型多级数据帧的总和。
以下是一个示例:
df1 = mul_df(5000,30,400) # mul_df to create a big multilevel dataframe
#let df2, df3, df4 = df1, df1, df1 to minimize the memory usage,
#they can also be mul_df(5000,30,400)
df2, df3, df4 = df1, df1, df1
In [12]: timeit df1+df2+df3+df4
1 loops, best of 3: 993 ms per loop
我对993ms不满意,有没有办法加快速度? cython可以改善性能吗?如果是的话,如何编写cython代码?感谢。
注意:
mul_df()
是创建演示多级数据帧的功能。
import itertools
import numpy as np
import pandas as pd
def mul_df(level1_rownum, level2_rownum, col_num, data_ty='float32'):
''' create multilevel dataframe, for example: mul_df(4,2,6)'''
index_name = ['STK_ID','RPT_Date']
col_name = ['COL'+str(x).zfill(3) for x in range(col_num)]
first_level_dt = [['A'+str(x).zfill(4)]*level2_rownum for x in range(level1_rownum)]
first_level_dt = list(itertools.chain(*first_level_dt)) #flatten the list
second_level_dt = ['B'+str(x).zfill(3) for x in range(level2_rownum)]*level1_rownum
dt = pd.DataFrame(np.random.randn(level1_rownum*level2_rownum, col_num), columns=col_name, dtype = data_ty)
dt[index_name[0]] = first_level_dt
dt[index_name[1]] = second_level_dt
rst = dt.set_index(index_name, drop=True, inplace=False)
return rst
更新:
我的Pentium双核T4200 @2.00GHZ,3.00GB RAM,WindowXP,Python 2.7.4,Numpy 1.7.1,Pandas 0.11.0,numexpr 2.0.1(Anaconda 1.5.0(32位) )
In [1]: from pandas.core import expressions as expr
In [2]: import numexpr as ne
In [3]: df1 = mul_df(5000,30,400)
In [4]: df2, df3, df4 = df1, df1, df1
In [5]: expr.set_use_numexpr(False)
In [6]: %timeit df1+df2+df3+df4
1 loops, best of 3: 1.06 s per loop
In [7]: expr.set_use_numexpr(True)
In [8]: %timeit df1+df2+df3+df4
1 loops, best of 3: 986 ms per loop
In [9]: %timeit DataFrame(ne.evaluate('df1+df2+df3+df4'),columns=df1.columns,index=df1.index,dtype='float32')
1 loops, best of 3: 388 ms per loop
答案 0 :(得分:9)
方法1:在我的机器上不是那么糟糕(禁用numexpr
)
In [41]: from pandas.core import expressions as expr
In [42]: expr.set_use_numexpr(False)
In [43]: %timeit df1+df2+df3+df4
1 loops, best of 3: 349 ms per loop
方法2:使用numexpr
(如果安装了numexpr
,则默认启用)
In [44]: expr.set_use_numexpr(True)
In [45]: %timeit df1+df2+df3+df4
10 loops, best of 3: 173 ms per loop
方法3:直接使用numexpr
In [34]: import numexpr as ne
In [46]: %timeit DataFrame(ne.evaluate('df1+df2+df3+df4'),columns=df1.columns,index=df1.index,dtype='float32')
10 loops, best of 3: 47.7 ms per loop
使用numexpr
可以实现这些加速,因为:
((df1+df2)+df3)+df4
正如我上面所暗示的,对于某些类型的操作(在0.11中),大熊猫使用numexpr
,例如, df1 + df2
将以这种方式进行评估,但是您在此处给出的示例将导致多次调用numexpr
(这是方法2比方法1更快)。使用直接(方法3)ne.evaluate(...)
可以实现更快的速度。
请注意,在pandas 0.13(本周将发布0.12)中,我们实现了一个函数pd.eval
,这实际上与我上面的例子完全相同。请继续关注(如果你喜欢冒险,那么很快就会掌握这一点:https://github.com/pydata/pandas/pull/4037)
In [5]: %timeit pd.eval('df1+df2+df3+df4')
10 loops, best of 3: 50.9 ms per loop
最后,为了回答你的问题,cython
根本没有帮助; numexpr
在这类问题上非常有效(那就是是 cython有用的情况)
一个警告:为了使用直接的Numexpr方法,帧应该已经对齐(Numexpr在numpy数组上运行,并且对索引一无所知)。他们应该是一个单一的dtype
答案 1 :(得分:0)
以下是我的观察: -首先,我重现您的测试案例并得出其他结果。在Pandas的幕后使用numexpression可显着提高性能。 -其次,我按降序对四个DataFrame之一进行排序,然后重新运行所有情况。性能中断,此外,(按预期)对Pandas DataFrames进行numexpression评估会导致错误的结果。
此案例复制了您的案例。唯一的区别是,我创建了初始DataFrame实例的副本。因此,没有共享的内容。使用不同的对象(id)来确保numexpression可以处理它。
import itertools
import numpy as np
import pandas as pd
def mul_df(level1_rownum, level2_rownum, col_num, data_ty='float32'):
''' create multilevel dataframe, for example: mul_df(4,2,6)'''
index_name = ['STK_ID','RPT_Date']
col_name = ['COL'+str(x).zfill(3) for x in range(col_num)]
first_level_dt = [['A'+str(x).zfill(4)]*level2_rownum for x in range(level1_rownum)]
first_level_dt = list(itertools.chain(*first_level_dt)) #flatten the list
second_level_dt = ['B'+str(x).zfill(3) for x in range(level2_rownum)]*level1_rownum
dt = pd.DataFrame(np.random.randn(level1_rownum*level2_rownum, col_num), columns=col_name, dtype = data_ty)
dt[index_name[0]] = first_level_dt
dt[index_name[1]] = second_level_dt
rst = dt.set_index(index_name, drop=True, inplace=False)
return rst
df1 = mul_df(5000,30,400)
df2, df3, df4 = df1.copy(), df1.copy(), df1.copy()
pd.options.compute.use_numexpr = False
%%timeit
df1 + df2 + df3 + df4
564 ms ± 10.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
pd.options.compute.use_numexpr = True
%%timeit
df1 + df2 + df3 + df4
152 ms ± 1.47 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
import numexpr as ne
%%timeit
pd.DataFrame(ne.evaluate('df1 + df2 + df3 + df4'), columns=df1.columns, index=df1.index, dtype='float32')
66.4 ms ± 1.16 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
(df1 + df2 + df3 + df4).equals(pd.DataFrame(ne.evaluate('df1 + df2 + df3 + df4'), columns=df1.columns, index=df1.index, dtype='float32'))
True
在这里,我以降序对DataFrame之一进行排序,因此更改了索引并重新排列了数据帧内部numpy数组中的行。
import itertools
import numpy as np
import pandas as pd
def mul_df(level1_rownum, level2_rownum, col_num, data_ty='float32'):
''' create multilevel dataframe, for example: mul_df(4,2,6)'''
index_name = ['STK_ID','RPT_Date']
col_name = ['COL'+str(x).zfill(3) for x in range(col_num)]
first_level_dt = [['A'+str(x).zfill(4)]*level2_rownum for x in range(level1_rownum)]
first_level_dt = list(itertools.chain(*first_level_dt)) #flatten the list
second_level_dt = ['B'+str(x).zfill(3) for x in range(level2_rownum)]*level1_rownum
dt = pd.DataFrame(np.random.randn(level1_rownum*level2_rownum, col_num), columns=col_name, dtype = data_ty)
dt[index_name[0]] = first_level_dt
dt[index_name[1]] = second_level_dt
rst = dt.set_index(index_name, drop=True, inplace=False)
return rst
df1 = mul_df(5000,30,400)
df2, df3, df4 = df1.copy(), df1.copy(), df1.copy().sort_index(ascending=False)
pd.options.compute.use_numexpr = False
%%timeit
df1 + df2 + df3 + df4
1.36 s ± 67.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
pd.options.compute.use_numexpr = True
%%timeit
df1 + df2 + df3 + df4
928 ms ± 39.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
import numexpr as ne
%%timeit
pd.DataFrame(ne.evaluate('df1 + df2 + df3 + df4'), columns=df1.columns, index=df1.index, dtype='float32')
68 ms ± 2.2 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
(df1 + df2 + df3 + df4).equals(pd.DataFrame(ne.evaluate('df1 + df2 + df3 + df4'), columns=df1.columns, index=df1.index, dtype='float32'))
False
通过使用numexpr
2 * df1
,则同样如此。