【问题标题】:Calculating local means in a 1D numpy array计算一维 numpy 数组中的局部均值
【发布时间】:2015-10-08 03:01:01
【问题描述】:

我有如下的一维 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]

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

【问题讨论】:

  • 你是如何选择这些数字 1,2,6,7 的?规则是什么?
  • @omri_saadon 两个连续元素,5 个元素之后是另外两个连续元素。
  • 我可以看到你这里有插值数据,可能是图像数据。为什么不重新表述这个问题,以便清楚地知道这 2 组中的每组需要哪个序列?
  • @gburton 好的,我做到了
  • @Borys - 没问题。我有正确的答案,但删除了我的帖子,因为我不确定我是否做对了......但现在我知道我做对了!看看吧。

标签: python arrays numpy scipy mean


【解决方案1】:

您可以从信号处理的角度来解决这个问题,而不是索引。您基本上是在使用 7 抽头内核执行输入信号的discrete convolution,其中三个中心系数为 0,而末端为 1,并且由于您要计算平均值,因此您需要将所有值乘以 @ 987654325@。但是,您并没有计算所有元素的卷积,但我们稍后会解决这个问题。 一种方法是为此使用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 个这样的序列,我们只是在这 4 个序列上 zip,其中每个序列跳过其他元素,但是我们从一个偏移量开始。第一个序列从偏移量 0 开始,下一个从偏移量 1 开始,下一个从偏移量 5 开始,下一个从偏移量 6 开始。我们收集这四个元素并对它们进行平均,然后跳过数组中的每个元素,直到完成。

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

【讨论】:

【解决方案2】:

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

【讨论】:

  • 非常酷的把戏……每天离开 MATLAB,让我更喜欢 numpy。我更喜欢卷积,但很高兴看到......
猜你喜欢
  • 2020-12-09
  • 2019-03-02
  • 1970-01-01
  • 2021-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-13
相关资源
最近更新 更多