I have the following signal: enter image description here

I know, that there are 2 states ('High' and 'Low') in my real signal. I need to convert my signal to the sequence of these states (Low from 2 to 5 second, High from 5.5 to 7 and so on).

Could you give me a piece of advice about classic and well established method to extract such signal from mine?

Median filter was as an example. Is it the most classic way to do it? Or you may provide some information for a better approach?

I don't like the median filter approach, because I should rather voluntary define kernel and 2 thresholds. I am searching for a method, with more natural parameters.


Some knowledge about signal:

Amplitude and mean duration of the state may vary from approx. 1 to 3 fold. But I have some knowledge about CV and skewness/CV. CV~0.5-0.9, skewness/CV~2. Distribution of States duration is gamma-like.


1 Answer 1


Depending on your noise model, a median filter might be quite good or not. Have a look at this example:

import scipy.signal

# from http://stackoverflow.com/questions/23289976/how-to-find-zero-crossings-with-hysteresis
def hyst(x, th_lo, th_hi, initial = False):
    hi = x >= th_hi
    lo_or_hi = (x <= th_lo) | hi
    ind = np.nonzero(lo_or_hi)[0]
    if not ind.size: # prevent index error if ind is empty
        return np.zeros_like(x, dtype=bool) | initial
    cnt = np.cumsum(lo_or_hi) # from 0 to len(x)
    return np.where(cnt, hi[ind[cnt-1]], initial)

Fs = 300
T = 9
a = np.zeros(T*Fs)
# setup the state changes
a[0*Fs] = 1
a[0.5*Fs] = -2
a[2*Fs] = 2
a[3*Fs] = -2
a[4.5*Fs] = 2
a[7*Fs] = -2
a[8*Fs] = 2
a[8.5*Fs] = -2
# create the rect-waveform
data = np.cumsum(a)

# add some noise to the signal
rx = data + 2*np.random.randn(len(data))

plt.plot(data, 'r', lw=2)

# plot the filtered signal (if you have access to the purple signal in your plot, just use this signal)
filtered = scipy.signal.medfilt(rx, Fs//2+1)
plt.plot(filtered, lw=3)

# Perform thresholding with Hysteresis
detected = 2*hyst(filtered, -0.3, 0.3)-1

# From the thresholds, get the state changes via the derivative
diffed = np.diff(detected)
print ("estimated times of state switch:", ["%.2f" % (x/Fs) for x in np.nonzero(diffed)[0]])
print ("actual times of state switch:", ["%.2f" % (x/Fs) for x in np.nonzero(a)[0]])

enter image description here

estimated times of state switch: ['0.18', '0.60', '2.05', '3.10', '4.56', '7.07', '8.07', '8.59']
actual times of state switch: ['0.00', '0.50', '2.00', '3.00', '4.50', '7.00', '8.00', '8.50']

It creates an artificial signal, adds some noise, does a recovery of the original signal via median filtering. Eventually, it detects the state changes in the signal.

  • $\begingroup$ Thanks, but I was mainly interested is there is something smarter, than median filter? $\endgroup$
    – zlon
    Mar 29, 2017 at 6:52
  • $\begingroup$ What's the problem with median filter, if it works? $\endgroup$ Mar 29, 2017 at 6:53
  • $\begingroup$ I should define the size of the kernel and thresholds for each signal. I want some algorithm with a more natural parameters definition. $\endgroup$
    – zlon
    Mar 29, 2017 at 6:55
  • 1
    $\begingroup$ You can fit a statistical model. Do you have access to the minimum/maximum time of a state, including the probability of each length (e.g. is it equally likely that a state remains for 5 seconds or 1 second)? Dou you have knowledge of the amplitude of the state? $\endgroup$ Mar 29, 2017 at 7:08
  • $\begingroup$ Amplitude and mean duration of the state may vary from approx. 1 to 3 fold. But I have some knowlege about CV and skewness/CV. CV~0.5-0.9, skewnes/CV~2. Distribution of States duration is gamma-like. $\endgroup$
    – zlon
    Mar 29, 2017 at 7:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.