计算一维 numpy 数组中的局部均值

Calculating local means in a 1D numpy array

我有如下的一维 NumPy 数组:

import numpy as np
d = np.array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20])

我想计算 (1,2,6,7)、(3,4,8,9) 等的均值。 这涉及4个元素的平均值:两个连续元素和两个连续元素5个位置之后。

我尝试了以下方法:

>> import scipy.ndimage.filters as filt
>> res = filt.uniform_filter(d,size=4)
>> print res
[ 1  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]

不幸的是,这并没有给我想要的结果。我该怎么做?

您可以从信号处理的角度来解决这个问题,而不是建立索引。您基本上是使用 7 抽头内核对输入信号执行 discrete convolution,其中三个中心系数为 0,而四肢为 1,并且由于您想要计算平均值,因此需要将所有值相乘通过 (1/4)。但是,您并不是在计算所有元素的卷积,但我们稍后会解决这个问题。 一种方法是为此使用 scipy.ndimage.filters.convolve1d

import numpy as np
from scipy.ndimage import filters
d = np.arange(1, 21, dtype=np.float)
ker = (1.0/4.0)*np.array([1,1,0,0,0,1,1], dtype=np.float)
out = filters.convolve1d(d, ker)[3:-3:2]

因为您使用的是 7 抽头内核,卷积会将输出向左扩展 3 个,向右扩展 3 个,因此您需要确保裁剪掉第一个和最后三个元素。您还希望 跳过 每隔一个元素,因为卷积涉及滑动 window,但您希望丢弃所有其他元素以便获得所需的结果。

我们得到这个 out:

In [47]: out
Out[47]: array([  4.,   6.,   8.,  10.,  12.,  14.,  16.])

要仔细检查我们的结果是否正确,请尝试对每个元素进行一些示例计算。第一个元素等于 (1+2+6+7)/4 = 4。第二个元素等于(3+4+8+9)/4 = 6,以此类推


要获得较少麻烦的解决方案,请尝试使用 mode=valid 标志的 numpy.convolve。这避免了剪切掉左右两侧的额外填充,但是您仍然需要跳过所有其他元素:

import numpy as np
d = np.arange(1, 21, dtype=np.float)
ker = (1.0/4.0)*np.array([1,1,0,0,0,1,1], dtype=np.float)
out = np.convolve(d, ker, mode='valid')[::2]

我们还得到:

In [59]: out
Out[59]: array([  4.,   6.,   8.,  10.,  12.,  14.,  16.])

最后,如果你想要索引,这样的东西可能就足够了:

length = len(d[6::2])
out = np.array([(a+b+c+e)/4.0 for (a,b,c,e) in zip(d[::2][:length], d[1::2][:length], d[5::2][:length], d[6::2])])

我们得到:

In [69]: out
Out[69]: array([  4.,   6.,   8.,  10.,  12.,  14.,  16.])

这真的很难看,但它确实有效。信号的总长度取决于每个 window 的末尾位于第 7 个索引的事实。包含这些索引的数组的长度决定了信号的最终长度。另外请注意,对于 window 中的元素,可以通过跳过所有其他元素直到数组末尾来找到它的下一个元素。总共有 4 个这样的序列,我们简单地 zip 遍历这 4 个序列,其中每个序列跳过所有其他元素,但是我们从一个偏移量开始。第一个序列从偏移量 0 开始,下一个从 1 开始,下一个从 5 开始,下一个从 6 开始。我们收集这四个元素并对它们进行平均,然后跳过数组中的每个元素,直到完成。

顺便说一句,我还是比较喜欢卷积

您可以使用 numpy.lib.stride_tricks.as_strided() 获得适用于更一般情况的分组数组:

import numpy as np
from numpy.lib.stride_tricks import as_strided

d = np.arange(1, 21)

consec = 2
offset = 5
nsub = 2
pace = 2

s = d.strides[0]
ngroups= (d.shape[0] - (consec + (nsub-1)*offset - 1))//pace
a = as_strided(d, shape=(ngroups, nsub, consec),
               strides=(pace*s, offset*s, 1*s))

其中:

  • consec是子组中连续数字的个数
  • offset每个子组中第一个条目之间的偏移量
  • nsub子组数(1, 2为一个子组,与第二个子组6, 7相隔offset
  • pace 表示两组的第一个条目之间的步幅,在您的情况下是 pace=consec,但在更一般的情况下可能会有所不同

在您的情况下(使用给定值)a 将是:

array([[[ 1,  2],
        [ 6,  7]],

       [[ 3,  4],
        [ 8,  9]],

       [[ 5,  6],
        [10, 11]],

       [[ 7,  8],
        [12, 13]],

       [[ 9, 10],
        [14, 15]],

       [[11, 12],
        [16, 17]],

       [[13, 14],
        [18, 19]]])

从那里可以通过以下方式获得所需的平均值:

a.mean(axis=-1).mean(axis=-1)

#array([  4.,   6.,   8.,  10.,  12.,  14.,  16.])