【问题标题】:Numpy version of rolling maximum in pandas大熊猫滚动最大值的numpy版本
【发布时间】:2019-10-06 09:45:09
【问题描述】:

TL;DR:我的问题是如何改进我的功能以超越 pandas 自身的最大移动功能?


背景信息:

所以我正在处理大量移动平均线、移动最大值和移动最小值等,到目前为止,我发现的唯一移动窗口之类的功能位于 pandas.rolling method。问题是:我拥有的数据是 numpy 数组,我想要的最终结果也必须在 numpy 数组中;尽管我想简单地将其转换为 pandas 系列并返回 numpy 数组来完成这样的工作:

result2_max = pd.Series(data_array).rolling(window).max().to_numpy()

,转换数据类型似乎没有必要,而且在 numpy 实现中可能有一些方法可以做完全相同的事情。

然而,尽管它看起来很不合 Python,但它比我想出的或在网上看到的任何方法都快。我将在下面给出小基准:

import numpy as np
import pandas as pd

def numpy_rolling_max(data, window):

    data = data[::-1]
    data_strides = data.strides[0]

    movin_window = np.lib.stride_tricks.as_strided(data, 
                                                    shape=(data.shape[0] - window +1, window), 
                                                    strides = (data_strides ,data_strides)
                                                    )[::-1]
    max_window =np.amax(movin_window, axis = 1)#this line seems to be the bottleneck


    nan_array = np.full(window - 1, np.nan)
    return np.hstack((nan_array, max_window))


def pandas_rolling_max(data, window):
    return pd.Series(data).rolling(window).max().to_numpy()

length = 120000
window = 190
data = np.arange(length) + 0.5

result1_max = numpy_rolling_max(data, window)#21.9ms per loop
result2_max = pandas_rolling_max(data, window)#5.43ms per loop

result_comparision = np.allclose(result1_max, result2_max, equal_nan = True)

在 arraysize = 120k,window = 190 的情况下,pandas 滚动最大值比 numpy 版本快大约 3 倍。我不知道从哪里开始,因为我已经尽可能地矢量化了我自己的函数,但它仍然比 pandas 版本慢得多,我真的不知道为什么。

提前谢谢你

编辑:我找到了瓶颈,就是这一行:

max_window =np.amax(movin_window, axis = 1)

但是看到已经是向量化的函数调用,我还是不知道怎么继续。

【问题讨论】:

  • 你在 numpy 中尝试过convolve
  • @WeNYoBen 我没有,也不知道 convolve 有什么帮助....你介意告诉我怎么做吗?
  • @WeNYoBen 检查了它,不幸的是,熊猫默认版本似乎优于该帖子下面的答案; np.convolve 也不会在以下解决方案的任何部分中发挥作用:/
  • 你试用过 Scipy 版本吗(从 scipy.ndimage.filters 导入 maximum_filter1d 之一)-stackoverflow.com/a/43288787

标签: python pandas performance numpy rolling-computation


【解决方案1】:

我们可以使用1D max filter from Scipy 来复制与pandas 相同的行为,并且仍然更高效 -

from scipy.ndimage.filters import maximum_filter1d

def max_filter1d_same(a, W, fillna=np.nan):
    out_dtype = np.full(0,fillna).dtype
    hW = (W-1)//2 # Half window size
    out = maximum_filter1d(a,size=W, origin=hW)
    if out.dtype is out_dtype:
        out[:W-1] = fillna
    else:
        out = np.concatenate((np.full(W-1,fillna), out[W-1:]))
    return out

示例运行 -

In [161]: np.random.seed(0)
     ...: a = np.random.randint(0,999,(20))
     ...: window = 3

In [162]: a
Out[162]: 
array([684, 559, 629, 192, 835, 763, 707, 359,   9, 723, 277, 754, 804,
       599,  70, 472, 600, 396, 314, 705])

In [163]: pd.Series(a).rolling(window).max().to_numpy()
Out[163]: 
array([ nan,  nan, 684., 629., 835., 835., 835., 763., 707., 723., 723.,
       754., 804., 804., 804., 599., 600., 600., 600., 705.])

In [164]: max_filter1d_same(a,window)
Out[164]: 
array([ nan,  nan, 684., 629., 835., 835., 835., 763., 707., 723., 723.,
       754., 804., 804., 804., 599., 600., 600., 600., 705.])

# Use same dtype fillna for better memory efficiency
In [165]: max_filter1d_same(a,window,fillna=0)
Out[165]: 
array([  0,   0, 684, 629, 835, 835, 835, 763, 707, 723, 723, 754, 804,
       804, 804, 599, 600, 600, 600, 705])

实际测试用例大小的时间安排 -

In [171]: # Actual test-cases sizes
     ...: np.random.seed(0)
     ...: data_array = np.random.randint(0,999,(120000))
     ...: window = 190

In [172]: %timeit pd.Series(data_array).rolling(window).max().to_numpy()
100 loops, best of 3: 4.43 ms per loop

In [173]: %timeit max_filter1d_same(data_array,window)
100 loops, best of 3: 1.95 ms per loop

【讨论】:

  • 1 票还不够!我刚刚使用 2 个不同的数据数组在 1 到 1000 的窗口大小上循环测试了答案,结果是匹配的,速度提高了 200% 以上。谢谢楼主
  • 不过我还有一个问题。 scipy 中 max/min filter1d 下面的“秘密”是什么,它使得在移动窗口上计算 max/min 的速度如此之快?这样的“秘密”是否可以应用于任何功能,而不仅仅是最大和最小,也适用于一般的移动窗口?我对 numpy 的东西还很陌生,如果可能的话,我想了解更多。
  • @mathguy 认为它是在 C 中实现的,并且被定制为使用数组数据,因此在利用数组数据的空间局部性时它应该是有效的。
  • 如果我要编写另一个将应用于移动窗口的函数,是否有任何方法可以使用 cython 或任何等效物来利用数组数据的空间局部性?如果有,那么对于任何 pandas 滚动计算的东西来说,这将是一个巨大的进步
  • @mathguy Cython 肯定会有所帮助,AFAIK 会利用空间局部性。所以,一定要试一试。
猜你喜欢
  • 2017-08-27
  • 1970-01-01
  • 2017-10-05
  • 2019-10-26
  • 1970-01-01
  • 2021-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多