Pandas数据框中如何更快地执行rolling_apply操作?

3

改进这个问题,该问题提供了一种在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,这可以应用于更广泛的问题集。

编辑:性能计时校正


那是325微秒,相对于1秒,快了1000多倍,你意识到了吗? - EdChum
实际上,我甚至没有注意到那里的“µ”...感谢您的纠正。 - bazel
2个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
7
计算大小为n的数组上大小为m的滚动函数大约需要O(n*m)时间。内置的rollin_xxx方法使用了一些非常聪明的算法,使运行时间远远低于此,并且通常可以保证O(n)时间,如果你考虑它,这是一件相当令人印象深刻的事情。 特别是rolling_minrolling_maxbottleneck中借用了它们的实现,该文引用Richard Harter作为该算法的来源,尽管我发现在this paper中有一个更早的描述相同算法的论文。 所以在历史课之后:很可能你不能既拥有蛋糕又吃掉它。 rolling_apply 很方便,但几乎总是会牺牲特定算法的性能。根据我的经验,使用 Python 科学栈中更令人愉快的部分之一就是想出高效的计算方法,以创造性的方式使用快速原语。您自己的解决方案两次调用 rolling_max 是一个很好的例子。所以放松心情,享受这段旅程,知道如果您或 SO 的好人们无法提供更聪明的解决方案,您总是可以借助 rolling_apply

谢谢 - 我完全忘记了 bottleneck 模块 - 这解释了为什么双重滚动最大值如此之快。我只是想知道上面的原始策略是否可以改进,比如说如果 rolling_apply 可以接受一个大于一维的 ndarray。那么我们就不必在 meanmax 函数内部进行 iloc 操作,也不需要额外的 lambda 调用了。 - bazel

3

您可能无法达到rolling_max的速度,但是通过使用.values降至numpy通常可以减少一个数量级左右:

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倍,但比原始情况快得多。有时候,只需要将某些东西加速十倍,就可以使其不再是主要的时间浪费。

很好的发现。我仍在寻找一些通用的神奇方法(也许不存在),可以应用于需要rolling_apply的问题,但不管怎样,这个示例对于numpy的使用都是有益的 - 谢谢。 - bazel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,