Search code examples
pythonscipysamplingdownsampling

How to downsample a signal preserving spikes?


I'm analyzing a signal sampled at 200Hz for 6-8 seconds, and the important part are the spikes, that lasts 1 second at max. Think for example to an earthquake...

I have to downsample the signal by a factor 2. I tried:

from scipy import signal

signal.decimate(mysignal, 2, ftype="fir")
signal.resample_poly(mysignal, 1, 2)

I get the same result with both the functions: the signal is resampled, but the spikes, positive and negative ones, are diminished.

I wrong the function, or I have to pass a custom FIR filter?


Solution

  • Note

    Downsampling will always damage the signal if you hit the limits of sampling frequency with the frequency of your signal (Nyquist-Shannon sampling theorem). In your case, your spikes are similar to very high frequency signals, therefore you also need very high sampling frequency.

    (Example: You have 3 points where middle one has spike. You want to downsample it to 2 points. Where to put the spike? Nowhere, because you run out of samples.)

    Nevertheless, if you really want to downsample the signal and still you want to preserve (more or less accurately) particular points (in your case spikes), you can try below attitude, which 'saves' your spikes, downsample the signal and only afterwards applies the 'saved' spikes on corresponding downsampled signal positions.

    Steps to do:

    1) Get spikes, or in other words,local maximums(or minimums).

    example: Pandas finding local max and min

    2) Downsample the signal

    3) With those spikes you got from 1), replace the corresponding downsampled values

    (count with the fact that your signal will be damaged. You cant downsample without losing spikes that are represented by one or two points)

    EDIT

    Ilustrative example

    This is example how to keep the spikes. Its just example, as it is now it doesnt work for negative values

    enter image description here

    import numpy as np
    import matplotlib.pyplot as plt
    from collections import deque
    
    
    t = np.arange(1000)/100
    y = np.sin(t*2*3.14)
    y[150]=5
    y[655]=5
    y[333]=5
    y[250]=5
    
    def downsample(factor,values):
        buffer_ = deque([],maxlen=factor)
        downsampled_values = []
        for i,value in enumerate(values):
            buffer_.appendleft(value)
            if (i-1)%factor==0:
                #Take max value out of buffer
                # or you can take higher value if their difference is too big, otherwise just average
                downsampled_values.append(max(buffer_))
        return np.array(downsampled_values)
    
    plt.plot(downsample(10,y))
    plt.show()
    

    enter image description here