Numpy中一维数组的滚动窗口?

67

有没有一种有效的方法在Numpy中实现1D数组的滚动窗口?

例如,我有这个纯Python代码片段用于计算1D列表的滚动标准差,其中observations是值的1D列表,n是标准差的窗口长度:

stdev = []
for i, data in enumerate(observations[n-1:]):
    strip = observations[i:i+n]
    mean = sum(strip) / n
    stdev.append(sqrt(250*sum([(s-mean)**2 for s in strip])/(n-1)))

有没有一种完全在Numpy内部完成的方法,即不使用任何Python循环?使用numpy.std可以轻松计算标准差,但是滚动窗口部分完全使我困惑。
我找到了这篇关于Numpy中滚动窗口的博客文章,但似乎不适用于1D数组。

1
你也可以看一下bottleneck项目,它内置了移动平均、标准差等功能。 - derchambers
7个回答

83

只需使用博客代码,然后将您的函数应用于结果。

即:

numpy.std(rolling_window(observations, n), 1)

你在博客中看到的代码:

def rolling_window(a, window):
    shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
    strides = a.strides + (a.strides[-1],)
    return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)

1
这个能扩展到 n 维数组的轴吗? - Gulzar
1
还有如何使用它来创建步幅重叠? - Gulzar
@Gulzar,如果你还没有看过我的回答,请查看下面的答案。so12311的函数实际上可以在n维数组上沿着最后一个轴移动窗口。我的函数适用于沿第一个轴移动窗口的n维数组。如果您需要沿中间轴滚动,则需要拆分strides和shape,并在适当的位置添加额外的元素。我不知道您所说的重叠是什么意思,但您可以使用不同的stride和shape来实现它。考虑提出一个新问题,详细说明您需要的内容。 - Leland Hepworth
1
@LelandHepworth 谢谢。我所说的重叠是指例如对于数组[1, 2, 3, 4, 5],我想要一些方法来获取 [1, 2, 3][2, 3, 4][3, 4, 5] 等。 - Gulzar
1
@Gulzar,使用任何一个版本的函数,以下代码可以帮助您得到您想要的结果:rolling_window(np.array([1, 2, 3, 4, 5]), 3) - Leland Hepworth

45

Numpy 1.20开始,你可以直接使用sliding_window_view函数获得滑动窗口:

from numpy.lib.stride_tricks import sliding_window_view

sliding_window_view(np.array([1, 2, 3, 4, 5, 6]), window_shape = 3)
# array([[1, 2, 3],
#        [2, 3, 4],
#        [3, 4, 5],
#        [4, 5, 6]])

并且为了添加步幅,可以使用切片: sliding_window_view(np.array([1, 2, 3, 4, 5, 6]), window_shape = 3)[::stride] 我知道 stride_tricks 实现了这个功能,但是警告信息让我不敢使用它,而这个技巧处理得很好。 - Hephaestus

14

我尝试使用so12311上面列出的答案在一个形状为[样本数,特征数]的二维数组上,以便获得一个形状为[样本数,时间步数,特征数]的输出数组,用于卷积或lstm神经网络,但它并没有完全正确地工作。经过深入研究步幅如何工作,我意识到它将窗口沿着最后的轴移动,所以我进行了一些调整,使得窗口沿着第一轴移动:

def rolling_window(a, window_size):
    shape = (a.shape[0] - window_size + 1, window_size) + a.shape[1:]
    strides = (a.strides[0],) + a.strides
    return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)

注意:如果您只使用1D输入数组,则输出没有任何区别。在我的搜索中,这是最接近我想要做的事情的第一个结果,所以我将其添加到帮助其他搜索类似答案的人。


13

只需一行代码...

import pandas as pd

pd.Series(observations).rolling(n).std()

7

根据后续回答,这里添加了代码,用于滚动1-D numpy数组并选择窗口大小窗口步长频率

a = np.arange(50)

def rolling_window(array, window_size,freq):
    shape = (array.shape[0] - window_size + 1, window_size)
    strides = (array.strides[0],) + array.strides
    rolled = np.lib.stride_tricks.as_strided(array, shape=shape, strides=strides)
    return rolled[np.arange(0,shape[0],freq)]

rolling_window(a,10,5)

输出:

array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [ 5,  6,  7,  8,  9, 10, 11, 12, 13, 14],
       [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
       [15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [25, 26, 27, 28, 29, 30, 31, 32, 33, 34],
       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
       [35, 36, 37, 38, 39, 40, 41, 42, 43, 44],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]])


3
def moving_avg(x,n):
    mv =  np.convolve(x,np.ones(n)/n,mode='valid')
    return np.concatenate(([np.NaN for k in range(n-1)],mv))

0
我需要一个滚动窗口来应用到 n 维数组的任何中间轴上,因此我扩展了已经被接受的答案和 @Miguel Gonzalez 的代码。应用滚动窗口到 n 维数组沿任何轴的相应代码如下:
def rolling_window(array, window, freq, axis=0):
    shape = array.shape[:axis] + (array.shape[axis] - window_size + 1, window_size) + array.shape[axis+1:]
    strides = array.strides[:axis] + (array.strides[axis],) + array.strides[axis:]
    rolled = np.lib.stride_tricks.as_strided(array, shape=shape, strides=strides)
    return np.take(rolled, np.arange(0,shape[axis],freq), axis=axis)

创建一个测试示例以验证函数的有效性:
    arr = np.random.randint(1, 1000, size=(2,108,21,5))
    arr_windowed = rolling_window_ndimensional(arr, 12, 12, axis=1)

    print(arr.shape)
    print(arr_windowed.shape)
    np.allclose(arr, arr_windowed.reshape(2,-1, 21,5))

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