This can be done like so:
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)
Explanation: ind
are the indices of all the samples where the signal is below the lower or above the upper threshold, and for which the position of the 'switch' is thus well-defined. With cumsum
, you make some sort of counter which points to the index of the last well-defined sample. If the start of the input vector is between the two thresholds, cnt
will be 0, so you need to set the the corresponding output to the initial value using the where
function.
Credit: this is a trick I found in an old post on some Matlab forum, which I translated to Numpy. This code is a bit hard to understand and also needs to allocate various intermediate arrays. It would be better if Numpy would include a dedicated function, similar to your simple for-loop, but implemented in C for speed.
Quick test:
x = np.linspace(0,20, 1000)
y = np.sin(x)
h1 = hyst(y, -0.5, 0.5)
h2 = hyst(y, -0.5, 0.5, True)
plt.plot(x, y, x, -0.5 + h1, x, -0.5 + h2)
plt.legend(('input', 'output, start=0', 'output, start=1'))
plt.title('Thresholding with hysteresis')
plt.show()
Result:
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…