Let A
be a 1D numpy array, a threshold t
, and a window length K
.
How to find the minimal index j
, such that A[j:j+K] < t
? (i.e. the first time A stays below the threshold on a full window of width K).
I've tried (unfinished) things with a loop, but it seemed far from optimal, and I thought maybe there's a clever "numpy way" to do it.
Sidenote: the fact we want to test if we're below the threshold during a certain window length instead of ponctual value, is useful to avoid on/off/on/off/on/off artefacts near the threshold (see also Hysteresis: "hysteresis is intentionally added to an electronic circuit to prevent unwanted rapid switching [...] compensate for contact bounce in switches, or noise in an electrical signal.").
Approach #1
We can use 1D convolution
-
np.flatnonzero(np.convolve(A<t, np.ones(K,dtype=int))==K)[0]-K+1
The idea is to get the boolean array after comparison against threshold and then run a 1D
convolution with a kernel of length same as window
, filled with 1s
. This gives us sum of each sliding window. So, all windows that have sum of K
are the ones we are after. Use flatnonzero
to get the starting indices for the valid windows. Finally, select the first one.
Approach #2
With binary-erosion
-
from scipy.ndimage.morphology import binary_erosion
np.flatnonzero(binary_erosion(A<t, np.ones(K), origin=-(K//2)))[0]
This runs a sliding kernel with a length same as window
and erodes away all windows that don't have window
length True
ones in sequence, leaving us with the valid ones. Again, use flatnonzero
to get the indices and finally select the first one. We need to use the arg origin
with the binary-erosion, so that we select the starts.
Approach #3
Here's another with island finding -
# Get mask of valid elements with comparison against thresh
mask = np.r_[False,A<t,False]
# Get indices of starts and ends for the valid islands
idx = np.flatnonzero(mask[:-1] != mask[1:])
start,stop = idx[::2],idx[1::2]
# Get the island lengths and check for lengths >=K and mask start indices
# and select the first one among them
out = start[(stop - start)>=K][0]