信号的Numpy均方根(RMS)平滑

信号的Numpy均方根(RMS)平滑,numpy,iteration,scipy,smoothing,moving-average,Numpy,Iteration,Scipy,Smoothing,Moving Average,我有一个肌电图数据的信号,我应该(科学论文的明确建议)使用RMS平滑 我有下面的工作代码,生成所需的输出,但它比我认为可能的慢得多 #!/usr/bin/python import numpy def rms(interval, halfwindow): """ performs the moving-window smoothing of a signal using RMS """ n = len(interval) rms_signal = numpy.zeros(

我有一个肌电图数据的信号,我应该(科学论文的明确建议)使用RMS平滑

我有下面的工作代码,生成所需的输出,但它比我认为可能的慢得多

#!/usr/bin/python
import numpy
def rms(interval, halfwindow):
    """ performs the moving-window smoothing of a signal using RMS """
    n = len(interval)
    rms_signal = numpy.zeros(n)
    for i in range(n):
        small_index = max(0, i - halfwindow)  # intended to avoid boundary effect
        big_index = min(n, i + halfwindow)    # intended to avoid boundary effect
        window_samples = interval[small_index:big_index]

        # here is the RMS of the window, being attributed to rms_signal 'i'th sample:
        rms_signal[i] = sqrt(sum([s**2 for s in window_samples])/len(window_samples))

    return rms_signal
我看到了一些关于移动窗口循环优化的
deque
itertools
建议,也看到了numpy的
convolve
,但我不知道如何使用它们来实现我想要的

此外,我不想再避免边界问题了,因为我最终拥有了大型阵列和相对较小的滑动窗口


感谢阅读

,因为这不是一个线性变换,我认为不可能使用np.convalve()

这里有一个函数,它可以做你想做的事情。请注意,返回数组的第一个元素是第一个完整窗口的rms;i、 e.对于示例中的数组
a
,返回数组是子窗口
[1,2],[2,3],[3,4],[4,5]
的rms,不包括部分窗口
[1]
[5]

>>> def window_rms(a, window_size=2):
>>>     return np.sqrt(sum([a[window_size-i-1:len(a)-i]**2 for i in range(window_size-1)])/window_size)
>>> a = np.array([1,2,3,4,5])
>>> window_rms(a)
array([ 1.41421356,  2.44948974,  3.46410162,  4.47213595])

可以使用卷积来执行所指的操作。为了处理脑电信号,我也做了几次

将numpy导入为np
def窗口大小(a,窗口大小):
a2=np.功率(a,2)
窗口=np.ones(窗口大小)/浮动(窗口大小)
返回np.sqrt(np.convolve(a2,窗口'valid'))
分解后,
np.power(a,2)
部分生成一个新数组,其维数与
a
相同,但每个值都是平方的
np.one(window\u size)/float(window\u size)
生成一个数组或长度
window\u size
,其中每个元素
1/window\u size
。因此,卷积有效地产生了一个新数组,其中每个元素
i
等于

(a[i]^2 + a[i+1]^2 + … + a[i+window_size]^2)/window_size
它是移动窗口中数组元素的RMS值。这样应该表现得很好


但是请注意,
np.power(a,2)
生成了一个相同维度的新的数组。如果
a
真的大,我的意思是足够大,以至于它不能在内存中容纳两次,那么您可能需要一种策略,其中每个元素都会被修改到位。另外,
'valid'
参数指定放弃边界效果,从而使
np.convolve()
生成的数组更小。您可以通过指定
'same'
来保持这一切(请参阅)。

我发现我的机器在纠结卷积,因此我提出以下解决方案:

快速计算移动RMS窗口

假设我们有模拟电压样本a0。。。a99(一百个样本),我们需要通过它们获取10个样本的移动RMS。窗口将首先从元素a0扫描到a9(十个样本),以获得rms0。

简化它:我们有
a=[a0,…a99]
要创建10个样本的移动RMS,我们可以将10个
a^2
相加的sqrt乘以1/10。 换句话说,如果我们有

    p = (1/10) * a^2 = 1/10 * [a0^2, ... a99^2]
要获得
rms^2
只需添加一组10个p即可。

让我们有一个ACUMulator acu:

    acu = p0 + ... p8     # (as in note 1 above)
那我们就可以

    rms0^2 =  p0 + ...  p8 + p9 
           = acu + p9
    rms1^2 = acu + p9 + p10 - p0
    rms2^2 = acu + p9 + p10 + p11 - p0 - p1
    ...
我们可以创造:

    V0 = [acu,   0,   0, ...  0]
    V1 = [ p9, p10, p11, .... p99]          -- len=91
    V2 = [  0, -p0, -p1, ... -p89]          -- len=91

    V3 = V0 + V1 + V2
如果我们跑
itertools.accumulate(V3)
我们将获得rms阵列 代码:

将numpy导入为np
从itertools导入
a2=np.power(in_ch,2)/tm_w#创建p的数组,in_ch是样本,tm_w是窗口长度
v1=np.数组(a2[tm_w-1:])#v1=[p9,p10,…]
v2=np.append([0],a2[0:len(a2)-tm#w])#v2=[0,p0,…]
acu=list(累计(a2[0:tm_w-1])#获取窗口-1的初始累计(acu)
v1[0]=v1[0]+acu[-1]#rms元素#1将位于窗口末尾,并包含累加
rmspw2=列表(累积(v1-v2))
rms=净功率(rmspw2,0.5)

我可以在不到一分钟的时间内计算出128兆样本的数组。

你能链接到这篇论文吗?我从来没有听说过通过计算移动窗口上点的RMS来平滑信号。一般来说,这看起来不像原始信号的平滑版本。建议使用这种平滑方式,因为它与信号功率(能量)相关,这可以用来推断肌肉力量。Link:“提供振幅信息的另一种可接受的方法是“均方根”或RMS。与移动平均值一样,该量是针对特定时间间隔(移动窗口)T定义的,必须加以说明。”根据Noraxon手册(封闭源,我公司所有),这是平滑的首选方法时间窗口在50到100ms之间,或多或少。移动窗口的RMS也是音频电平表背后的想法。可以使用卷积,请参见我的答案。太好了,太好了,太好了,太聪明了!肌电信号比肌电信号短得多,4通道记录小于50mb,所以内存使用不会被禁止。我计划使用
'same'
,在堆栈中显示原始/平滑/滤波信号。只是为了记录,RMS现在快了500倍(用
cProfile测量的16个样本的
窗口大小为0.002 vs 1.000秒),而对于更大的(100+)窗口,RMS不会慢很多。我说过它很棒吗?你不应该使用你的代码指定任何东西,除了“有效”之外,因为
np的第二个参数。convolve()
包含整个窗口长度的倒数。只是为了扩展一下,可以让
window
成为一个和
1.0
一样的内核,如果需要更深奥的行为。实际上,函数中的三行代码执行一些DSP文本通常称之为“去线性化”、“解调”和“再线性化”,这可以用不同的功率(除两个外)、内核(除酉平方或高斯外)和
    V0 = [acu,   0,   0, ...  0]
    V1 = [ p9, p10, p11, .... p99]          -- len=91
    V2 = [  0, -p0, -p1, ... -p89]          -- len=91

    V3 = V0 + V1 + V2