I'm trying to write a function that would detect all rising edges - indexes in a vector where value exceeds certain threshold. Something similar is described here: Python rising/falling edge oscilloscope-like trigger, but I want to add hysteresis, so that trigger won't fire unless the value goes below another limit.
I came up the following code:
import numpy as np
arr = np.linspace(-10, 10, 60)
sample_values = np.sin(arr) + 0.6 * np.sin(arr*3)
above_trigger = sample_values > 0.6
below_deadband = sample_values < 0.0
combined = 1 * above_trigger - 1 * below_deadband
Now in the combined
array there is 1
where the original value was above upper limit, -1
where the value was below lower limit and 0
where the value was in between:
>>> combined
array([ 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 0, 0,
1, 1, 1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1,
0, 0, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1,
1, 0, 0, 1, 1, 1, 0, -1, -1])
My idea was to use some clever function that would process this vector sequentially and replace all sequences of zeros with whatever non-zero value was preceding them. Then the problem would boil down to simply finding where the value changes from -1
to 1
.
I though that greater
operation would fulfill this purpose if used correctly: -1
encoded as True
and 1
as False
:
True
("-1") > -1
) -> True
("-1")True
("-1") > 1
) -> False
("1")True
("-1") > 0
) -> True
("-1")False
("1") > -1
) -> True
("-1")False
("1") > 1
) -> False
("1")False
("1") > 0
) -> False
("1")But the results are not what I expect:
>>> 1 - 2 * np.greater.accumulate(combined)
array([-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1])
It seems that the greater
function doesn't correctly compare booleans with numeric values in this scenario, even though it works fine when used on scalars or pair-wise:
>>> np.greater(False, -1)
True
>>> np.greater.outer(False, combined)
array([False, False, True, True, True, True, True, True, True,
True, True, False, False, False, False, False, False, False,
False, False, False, True, True, True, True, True, True,
True, True, True, False, False, False, False, False, False,
False, False, False, True, True, True, True, True, True,
True, True, True, True, False, False, False, False, False,
False, False, False, False, True, True])
Is this expected behavior? Am I doing something wrong here, is there any way around this?
Alternatively, maybe you could suggest another approach to this problem?
Thank you.
I am not sure what the issue with np.greater.accumulate
is (it does not seem to behave as advertised indeed), but the following should work:
import numpy as np
import numpy as np
arr = np.linspace(-10, 10, 60)
sample_values = np.sin(arr) + 0.6 * np.sin(arr*3)
above_trigger = sample_values > 0.6
below_deadband = sample_values < 0.0
combined = 1 * above_trigger - 1 * below_deadband
mask = combined != 0
idx = np.where(mask,np.arange(len(mask)),0)
idx = np.maximum.accumulate(idx)
result = combined[idx]
print(f"combined:\n {combined}\n")
print(f"result:\n {result}")
It gives:
combined:
[ 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 1 1 1 0 0 1 1 1 0 -1 -1 -1
-1 -1 -1 -1 -1 -1 0 1 1 1 0 0 1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 1 1 1 0 0 1 1 1 0 -1 -1]
result:
[ 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1 1 1 1 1 1 1 1 1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 1 1 1 1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 1 1 1 1 1 1 1 1 1 -1 -1]
Then the indices where values jumps from -1 to 1 can be obtained as follows:
np.nonzero(result[1:] > result[:-1])[0] + 1
It gives:
array([12, 31, 49])