NumPy数组中的滑动窗口中的Max

时间:2017-04-07 23:39:56

标签: python performance numpy scipy max

我想创建一个数组,该数组包含移动通过给定numpy数组的窗口的所有max() es。如果这听起来令人困惑,我很抱歉。我举个例子。输入:

[ 6,4,8,7,1,4,3,5,7,2,4,6,2,1,3,5,6,3,4,7,1,9,4,3,2 ]

窗口宽度为5的输出应为:

[     8,8,8,7,7,7,7,7,7,6,6,6,6,6,6,7,7,9,9,9,9     ]

每个数字应为输入数组宽度为5的子数组的最大值:

[ 6,4,8,7,1,4,3,5,7,2,4,6,2,1,3,5,6,3,4,7,1,9,4,3,2 ]
  \       /                 \       /
   \     /                   \     /
    \   /                     \   /
     \ /                       \ /
[     8,8,8,7,7,7,7,7,7,6,6,6,6,6,6,7,7,9,9,9,9     ]

我没有在numpy中找到一个可以做到这一点的开箱即用的功能(但如果有的话,我不会感到惊讶;我并不总是在考虑numpy开发人员的想法)。我考虑创建一个移位的2D版本的输入:

[ [ 6,4,8,7,1,4,3,5,7,8,4,6,2,1,3,5,6,3,4,7,1 ]
  [ 4,8,7,1,4,3,5,7,8,4,6,2,1,3,5,6,3,4,7,1,9 ]
  [ 8,7,1,4,3,5,7,8,4,6,2,1,3,5,6,3,4,7,1,9,4 ]
  [ 7,1,4,3,5,7,8,4,6,2,1,3,5,6,3,4,7,1,9,4,3 ]
  [ 1,4,3,5,7,8,4,6,2,1,3,5,6,3,4,7,1,9,4,3,2 ] ]

然后我可以对此应用np.max(input, 0)并获得我的结果。但在我的情况下这似乎并不高效,因为我的数组和窗口宽度都可以很大(> 1000000条目和> 100000窗口宽度)。数据会被窗口宽度的因素或多或少地炸毁。

我还考虑过以某种方式使用np.convolve(),但无法找到实现目标的方法。

如何有效地做到这一点?

6 个答案:

答案 0 :(得分:6)

方法#1:您可以使用1D max filter from Scipy -

from scipy.ndimage.filters import maximum_filter1d

def max_filter1d_valid(a, W):
    hW = (W-1)//2 # Half window size
    return maximum_filter1d(a,size=W)[hW:-hW]

方法#2:这是另一种方法stridesstrided_app创建一个2D移位版本,可以非常有效地查看数组这应该让我们之后沿第二轴使用任何自定义缩小操作 -

def max_filter1d_valid_strided(a, W):
    return strided_app(a, W, S=1).max(axis=1)

运行时测试 -

In [55]: a = np.random.randint(0,10,(10000))

# @Abdou's solution using pandas rolling
In [56]: %timeit pd.Series(a).rolling(5).max().dropna().tolist()
1000 loops, best of 3: 999 µs per loop

In [57]: %timeit max_filter1d_valid(a, W=5)
    ...: %timeit max_filter1d_valid_strided(a, W=5)
    ...: 
10000 loops, best of 3: 90.5 µs per loop
10000 loops, best of 3: 87.9 µs per loop

答案 1 :(得分:6)

Pandas对Series和DataFrames都有滚动方法,可以在这里使用:

import pandas as pd

lst = [6,4,8,7,1,4,3,5,7,8,4,6,2,1,3,5,6,3,4,7,1,9,4,3,2]
lst1 = pd.Series(lst).rolling(5).max().dropna().tolist()

# [8.0, 8.0, 8.0, 7.0, 7.0, 8.0, 8.0, 8.0, 8.0, 8.0, 6.0, 6.0, 6.0, 6.0, 6.0, 7.0, 7.0, 9.0, 9.0, 9.0, 9.0]

为了保持一致性,您可以将lst1的每个元素强制转换为int

[int(x) for x in lst1]

# [8, 8, 8, 7, 7, 8, 8, 8, 8, 8, 6, 6, 6, 6, 6, 7, 7, 9, 9, 9, 9]

答案 2 :(得分:1)

首先,我认为你的解释中有一个错误,因为你解释开头的初始输入数组的第10个元素等于8,而在下面,你应用窗口时,它是2。 / p>

在纠正之后,我认为执行所需操作的代码如下:

import numpy as np
a=np.array([ 6,4,8,7,1,4,3,5,7,8,4,6,2,1,3,5,6,3,4,7,1,9,4,3,2 ])
window=5
for i in range(0,len(a)-window,1): 
    b[i] = np.amax(a[i:i+window])

我认为,这种方式比创建移植的2D版本更好,因为当您创建这样的版本时,您需要使用比使用原始输入数组更多的内存,因此如果输入您可能会耗尽内存很大。

答案 3 :(得分:1)

我现在尝试了几种变体,并宣称Pandas版本是此次性能竞赛的赢家。我尝试了几种变体,甚至使用二叉树(用纯Python实现)来快速计算任意子范围的最大值。 (可根据需要提供来源)。我自己提出的最好的算法是使用环形缓冲区的普通滚动窗口;如果在此迭代中从当中删除当前最大值,则只需要完全重新计算的最大值;否则它将保持或增加到下一个新值。与旧库相比,这种纯Python实现比其他实现更快。

最后,我发现相关库的版本具有高度相关性。我主要使用的相当旧的版本比现代版本慢。以下是1M数字的数字,rollingMax用一个大小为100k的窗口:

         old (slow HW)           new (better HW)
scipy:   0.9.0:  21.2987391949   0.13.3:  11.5804400444
pandas:  0.7.0:  13.5896410942   0.18.1:   0.0551438331604
numpy:   1.6.1:   1.17417216301  1.8.2:    0.537392139435

以下是使用ringbuffer的纯numpy版本的实现:

def rollingMax(a, window):
  def eachValue():
    w = a[:window].copy()
    m = w.max()
    yield m
    i = 0
    j = window
    while j < len(a):
      oldValue = w[i]
      newValue = w[i] = a[j]
      if newValue > m:
        m = newValue
      elif oldValue == m:
        m = w.max()
      yield m
      i = (i + 1) % window
      j += 1
  return np.array(list(eachValue()))

对于我的输入,这很有效,因为我正在处理所有方向都有很多峰值的音频数据。如果你把一个不断减少的信号放入其中(例如-np.arange(10000000)),那么你将遇到最坏的情况(也许你应该在这种情况下反转输入和输出)。

我只是包含这个,以防有人想在具有旧库的机器上执行此任务。

答案 4 :(得分:0)

如果您有二维数据(例如,股票价格)并且想要获得滚动最大值或其他值,则此方法有效。 无需迭代即可进行计算。

n = 5  # size of rolling window

data_expanded = np.expand_dims(data, 1)
data_shift = [np.roll(data_expanded, shift=-i, axis=2) for i in range(n)]
data_shift = np.concatenate(data_shift, axis=1)

data_max = np.max(data_shift, axis=1)  # max, mean, std...

答案 5 :(得分:0)

w = torch.rand(1, 2) w.requires_grad = True b = torch.rand(1) b.requires_grad = True 开始,sliding_window_view 提供了一种滑动/滚动元素窗口的方法。然后您可以找到最大值的 Windows:

Numpy 1.20

哪里:

  • from numpy.lib.stride_tricks import sliding_window_view # values = np.array([6,4,8,7,1,4,3,5,7,2,4,6,2,1,3,5,6,3,4,7,1,9,4,3,2]) np.max(sliding_window_view(values, window_shape = 5), axis = 1) # array([8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 7, 7, 9, 9, 9, 9]) 是滑动窗口的大小
  • window_shape 找出每个子数组的最大值

滑动的中间结果是:

np.max(array, axis = 1)