计算1D numpy数组中的局部均值

时间:2015-07-18 14:00:22

标签: python arrays numpy scipy mean

我有1D 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]

遗憾的是,这并没有给我预期的结果。我该怎么办?

2 个答案:

答案 0 :(得分:11)

您可以使用信号处理视角来处理此问题,而不是建立索引。您基本上使用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扩展到右边,所以你需要确保裁掉第一个和最后三个元素。你还想跳过所有其他元素,因为卷积涉及一个滑动窗口,但你想丢弃所有其他元素,以便得到你想要的结果。

我们为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.])

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

顺便说一句,我还是更喜欢卷积。

答案 1 :(得分:1)

您可以使用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.])