I have a signal of electromyographical data that I am supposed (scientific papers' explicit recommendation) to smooth using RMS.
I have the following working code, producing the desired output, but it is way slower than I think it's possible.
#!/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
I have seen some deque
and itertools
suggestions regarding optimization of moving window loops, and also convolve
from numpy, but I couldn't figure it out how to accomplish what I want using them.
Also, I do not care to avoid boundary problems anymore, because I end up having large arrays and relatively small sliding windows.
Thanks for reading
It is possible to use convolution to perform the operation you are referring to. I did it a few times for processing EEG signals as well.
Breaking it down, the
np.power(a, 2)
part makes a new array with the same dimension asa
, but where each value is squared.np.ones(window_size)/float(window_size)
produces an array or lengthwindow_size
where each element is1/window_size
. So the convolution effectively produces a new array where each elementi
is equal towhich is the RMS value of the array elements within the moving window. It should perform really well this way.
Note, though, that
np.power(a, 2)
produces a new array of same dimension. Ifa
is really large, I mean sufficiently large that it cannot fit twice in memory, you might need a strategy where each element are modified in place. Also, the'valid'
argument specifies to discard border effects, resulting in a smaller array produced bynp.convolve()
. You could keep it all by specifying'same'
instead (see documentation).Since this is not a linear transformation, I don't believe it is possible to use np.convolve().
Here's a function which should do what you want. Note that the first element of the returned array is the rms of the first full window; i.e. for the array
a
in the example, the return array is the rms of the subwindows[1,2],[2,3],[3,4],[4,5]
and does not include the partial windows[1]
and[5]
.