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

问题描述 投票:0回答:3

我有一个肌电图数据信号,我认为(科学论文明确推荐)可以使用 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
,但我不知道如何使用它们来完成我想要的事情。

此外,我不再关心避免边界问题,因为我最终会拥有大型数组和相对较小的滑动窗口。

感谢您的阅读

numpy iteration scipy smoothing moving-average
3个回答
19
投票

可以使用卷积来执行您所指的操作。我也这样做了几次来处理脑电图信号。 import numpy as np def window_rms(a, window_size): a2 = np.power(a,2) window = np.ones(window_size)/float(window_size) return np.sqrt(np.convolve(a2, window, 'valid'))

将其分解,
np.power(a, 2)

部分创建一个与

a
具有相同维度的新数组,但其中每个值都是平方的。
np.ones(window_size)/float(window_size)
生成一个数组或长度
window_size
,其中每个元素为
1/window_size
。因此,卷积有效地产生了一个新数组,其中每个元素
i
等于

(a[i]^2 + a[i+1]^2 + … + a[i+window_size]^2)/window_size

即移动窗口内数组元素的均方根值。这样应该表现得很好。

但请注意,

np.power(a, 2)

会生成相同维度的

new
数组。如果 a
真的
很大,我的意思是足够大以至于它不能在内存中容纳两次,您可能需要一个策略来就地修改每个元素。此外, 'valid' 参数指定丢弃边框效果,从而导致
np.convolve()
生成更小的数组。您可以通过指定
'same'
来保留所有内容(请参阅
文档
)。


1
投票

快速计算移动RMS窗口

假设我们有模拟电压样本 a0 ... a99(一百个样本),我们需要通过它们获取 10 个样本的移动 RMS。

窗口将首先从元素 a0 到 a9(十个样本)扫描以获得 rms0。

# rms = [rms0, rms1, ... rms99-9] (total of 91 elements in list): (rms0)^2 = (1/10) (a0^2 + ... + a9^2) # --- (note 1) (rms1)^2 = (1/10) (... a1^2 + ... + a9^2 + a10^2) # window moved a step, a0 falls out, a10 comes in (rms2)^2 = (1/10) ( a2^2 + ... + a10^2 + a11^2) # window moved another step, a1 falls out, a11 comes in ...

简化一下:我们有
a = [a0, ... a99]

要创建 10 个样本的移动 RMS,我们可以取 10 个

a^2
相加的开方,然后乘以 1/10。

换句话说,如果我们有

p = (1/10) * a^2 = 1/10 * [a0^2, ... a99^2]

要获得 
rms^2

,只需添加一组 10 个 p。

让我们有一个累加器 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)

我们将得到均方根数组


代码:

import numpy as np from itertools import accumulate a2 = np.power(in_ch, 2) / tm_w # create array of p, in_ch is samples, tm_w is window length v1 = np.array(a2[tm_w - 1 : ]) # v1 = [p9, p10, ...] v2 = np.append([0], a2[0 : len(a2) - tm_w]) # v2 = [0, p0, ...] acu = list(accumulate(a2[0 : tm_w - 1])) # get initial accumulation (acu) of the window - 1 v1[0] = v1[0] + acu[-1] # rms element #1 will be at end of window and contains the accumulation rmspw2 = list(accumulate(v1 - v2)) rms = np.power(rmspw2, 0.5)

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


0
投票

这是一个应该可以完成您想要的功能的函数。请注意,返回数组的第一个元素是第一个完整窗口的均方根;即对于示例中的数组

a

,返回数组是子窗口

[1,2],[2,3],[3,4],[4,5]
的均方根,不包括部分窗口
[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])

© www.soinside.com 2019 - 2024. All rights reserved.