For a data analysis task, I want to find zero crossings in a numpy array, coming from a convolution with first a sobel-like kernel, and then a mexican hat kernel. Zero crossings allow me to detect edges in the data.
Unfortunately, the data is somewhat noisy and I only want to find zero crossings with a minimal jump size, 20
in the follwing example:
import numpy as np
arr = np.array([12, 15, 9, 8, -1, 1, -12, -10, 10])
Should result in
>>>array([1, 3, 7])
or
>>>array([3, 7])
Where 3
is the index of -1
, just before the middle of the first jump and 7
is the index of -10
I have tried a modification of the following code (source: Efficiently detect sign-changes in python)
zero_crossings = np.where(np.diff(np.sign(np.trunc(arr/10))))[0]
Which correctly ignores small jumps, but puts the zero-crossings at [1,5,7]
What would be an efficient way of doing this?
The definition of minimal jump is not strict, but results should be along the lines of my question.
Edit: For Clarification
arr = np.array([12, 15, 9, 8, -1, 1, -12, -10, 10])
arr_floored = np.trunc(arr/10)
>>>>np.array([10, 10, 0, 0, 0, 0, -10, -10, 10])
sgn = np.sign(arr_floored)
>>>>array([ 1, 1, 0, 0, 0, 0, -1, -1, 1])
dsgn = np.diff(sgn)
>>>>array([ 0, -1, 0, 0, 0, -1, 0, 2])
np.where(dsgn)
>>>>(array([1, 5, 7], dtype=int64),)
Further edgecases:
arr = [10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10]
Should result in
>>> np.array([10])
Also just noticed: The problem might be ill-posed(in a mathematical sense). I will clarify it later today.
Find the zero crossing points in 1d data. Find the zero crossing events in a discrete data set. Linear interpolation is used to determine the actual locations of the zero crossing between two data points showing a change in sign. Data point which are zero are counted in as zero crossings if a sign change occurs across them.
Another way to count zero crossings and squeeze just a few more milliseconds out of the code is to use nonzero and compute the signs directly. Assuming you have a one-dimensional array of data:
Find the zero crossing events in a discrete data set. Linear interpolation is used to determine the actual locations of the zero crossing between two data points showing a change in sign. Data point which are zero are counted in as zero crossings if a sign change occurs across them.
As remarked by Jay Borseth the accepted answer does not handle arrays containing 0 correctly. Since a) using numpy.signbit () is a little bit quicker than numpy.sign (), since it's implementation is simpler, I guess and b) it deals correctly with zeros in the input array.
Here's a solution that gives the midpoint of crossings involving a noise threshold to filter potentially multiple fluctuations around zero applied across multiple data points. It give the correct answers for the two examples you supplied. However, I've made a couple of assumptions:
ABS(start | end) >= 10
hence I've used the minimum range where this condition holds.
import numpy as np
import pandas as pd
arr = np.array([12, 15, 9, 8, -1, 1, -12, -10, 10])
sgn = pd.Series(np.sign(np.trunc(arr/10)))
trailingEdge = sgn[sgn!=0].diff()
edgeIndex = np.array(trailingEdge[trailingEdge!=0].index)
edgeIndex[:-1] + np.diff(edgeIndex) / 2
gives:
array([3., 7.])
and
arr = [10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10]
gives:
array([10.])
I guess you want
import numpy as np
x = np.array([10, -50, -30, 50, 10, 3, -200, -12, 123])
indices = np.where(np.logical_and(np.abs(np.diff(x)) >= 20, np.diff(np.sign(x)) != 0))[0]
read as: indices, where ((absolute differences of x) are larger or equal 20) and (the sign flips)
which returns
array([0, 2, 5, 7])
The usual numpy functions don't cover this case. I would suggest simply adding the first element in the end, via the pad function:
import numpy as np
x = np.array([10, 5, 0, -5, -10])
x = np.pad(x, (0, 1), 'wrap')
indices = np.where(np.logical_and(np.abs(np.diff(x)) >= 20, np.diff(np.sign(x)) != 0))[0]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With