滚动MAD(平均绝对偏差)的Numpy版本

5
如何制作一个滚动版本的以下MAD函数
from numpy import mean, absolute

def mad(data, axis=None):
    return mean(absolute(data - mean(data, axis)), axis)

这段代码是回答这个问题的。

目前,我将numpy转换为pandas,然后应用此函数,最后将结果转换回numpy。

pandasDataFrame.rolling(window=90).apply(mad) 

但是在更大的数据框上使用这种方法效率低下。如何在numpy中获得滚动窗口以执行相同的函数而无需循环,并给出相同的结果?


不那么低效? - kmario23
嗯,你知道的,在我的脑海中我想到了其他的东西 :) 谢谢 - RaduS
1个回答

10

这里是一个使用向量化的NumPy方法 -

# From this post : https://dev59.com/7FkS5IYBdhLWcg3wSk3b#40085052
def strided_app(a, L, S ):  # Window len = L, Stride len/stepsize = S
    nrows = ((a.size-L)//S)+1
    n = a.strides[0]
    return np.lib.stride_tricks.as_strided(a, shape=(nrows,L), strides=(S*n,n))

# From this post : https://dev59.com/IWYq5IYBdhLWcg3wpyNE#14314054 by @Jaime
def moving_average(a, n=3) :
    ret = np.cumsum(a, dtype=float)
    ret[n:] = ret[n:] - ret[:-n]
    return ret[n - 1:] / n

def mad_numpy(a, W):
    a2D = strided_app(a,W,1)
    return np.absolute(a2D - moving_average(a,W)[:,None]).mean(1)

运行时测试 -

In [617]: data = np.random.randint(0,9,(10000))
     ...: df = pd.DataFrame(data)
     ...: 

In [618]: pandas_out = pd.rolling_apply(df,90,mad).values.ravel()
In [619]: numpy_out = mad_numpy(data,90)

In [620]: np.allclose(pandas_out[89:], numpy_out) # Nans part clipped
Out[620]: True

In [621]: %timeit pd.rolling_apply(df,90,mad)
10 loops, best of 3: 111 ms per loop

In [622]: %timeit mad_numpy(data,90)
100 loops, best of 3: 3.4 ms per loop

In [623]: 111/3.4
Out[623]: 32.64705882352941

使用循环的pandas解决方案相比,这里有一个巨大的32倍以上的加速


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