在Pandas DataFrame上更快的rolling_apply?

时间:2014-08-30 14:34:30

标签: python numpy pandas

改进this question,这为在DataFrame中的多个列上应用函数提供了一个聪明的解决方案,我想知道该解决方案是否可以进一步优化速度。

环境:Python 2.7.8,Pandas 14.1,Numpy 1.8。

以下是示例设置:

import pandas as pd
import numpy as np
import random

def meanmax(ii,df):
    xdf = df.iloc[map(int,ii)]
    n = max(xdf['A']) + max(xdf['B'])
    return n / 2.0

df  = pd.DataFrame(np.random.randn(2500,2)/10000, 
                    index=pd.date_range('2001-01-01',periods=2500),
                    columns=['A','B'])              
df['ii'] = range(len(df))      

res = pd.rolling_apply(df.ii, 26, lambda x: meanmax(x, df))

请注意meanmax函数不是成对的,因此rolling_mean(df['A'] + df['B'],26)之类的函数不会起作用。

但是我可以这样做:

res2 = (pd.rolling_max(df['A'],26) + pd.rolling_max(df['B'],26)) / 2

其完成速度提高了大约3000倍:

%timeit res = pd.rolling_apply(df.ii, 26, lambda x: meanmax(x, df))
1 loops, best of 3: 1 s per loop

%timeit res2 = (pd.rolling_max(df['A'],26) + pd.rolling_max(df['B'],26)) / 2
1000 loops, best of 3: 325 µs per loop

在给定示例函数并使用rolling_apply的情况下,是否有比上面第二个选项更好/等效的东西?虽然第二个选项更快,但它没有使用rolling_apply,这可以应用于更广泛的问题集

编辑:性能时序校正

2 个答案:

答案 0 :(得分:7)

使用大小为n的窗口计算大小为m的数组的通用滚动函数大约需要O(n*m)次。内置的rollin_xxx方法使用一些非常智能的算法来将运行时间保持在远低于此值,并且通常可以保证O(n)时间,如果你认为它是一个相当令人印象深刻的东西。

rolling_minrolling_max特别从bottleneck借用了它们的实现,它引用Richard Harter作为算法的来源,尽管我发现我认为是之前的描述this paper中的相同算法。

所以在历史课之后:你很可能不能吃蛋糕了。 rolling_apply非常方便,但它几乎总是会牺牲特定算法的性能。根据我的经验,使用Python科学堆栈的一个更令人愉快的部分是提出有效的计算方法,使用创造性方式提供的快速原语。您自己的解决方案两次调用rolling_max就是一个很好的例子。因此,如果您或SO的优秀人员无法提供更智能的解决方案,那么您应该放心并享受骑行的乐趣。{/ p>

答案 1 :(得分:3)

您将无法以rolling_max的速度降低速度,但通过numpy下降到.values,您通常可以减少一个数量级左右:

def meanmax_np(ii, df):
    ii = ii.astype(int)
    n = df["A"].values[ii].max() + df["B"].values[ii].max()
    return n/2.0

给了我

>>> %timeit res = pd.rolling_apply(df.ii, 26, lambda x: meanmax(x, df))
1 loops, best of 3: 701 ms per loop
>>> %timeit res_np = pd.rolling_apply(df.ii, 26, lambda x: meanmax_np(x, df))
10 loops, best of 3: 31.2 ms per loop
>>> %timeit res2 = (pd.rolling_max(df['A'],26) + pd.rolling_max(df['B'],26)) / 2
1000 loops, best of 3: 247 µs per loop

虽然比优化的情况慢100倍但比原始情况快得多。有时当我只需要快十倍的东西时,它就不会成为足够的主导时间段。