I'm looking for a function that finds the index of the zero-crossing points of a list. Before I go making my own subroutine to do this, I was wondering if anyone knows of any built-in Mathematica function for it.
Example of what I want:
list = {-2,-1,0,1,2,3,4,3,1,-2,-4,8,9,10};
ZeroCrossing[list] returns: {3,10,12}
Thanks,
EDIT:
Per whuber's suggestion, I'm adding my findings to the initial question, instead of just in a solution.
I've checked LabVIEW (the other language I know well), and it considers "Bounces" ({1,0,2}, {-2,0,1}) and duplicates ({1,0,0,2}) to be zero-crossings. It outputs a T/F value for each array index.
Example:
ZeroCrossing[{1,0,2}]
(* Returns: {F,T,T} *)
ZeroCrossing[{1,0,0,2,3}]
(* Returns: {F,T,T,T,F} *)
Answer
There are different kinds of zero crossings:
- ..., -1, 1, ... is a crossing between two values
- ..., -1, 0, 1, ... is a crossing at a zero
- ..., -1, 0, 0, ..., 0, 1, ... is a crossing for a range of zeros
and non zero crossings:
- ..., -1, 0, -1, ... is not a (transverse) crossing at all
- ..., -1, 0, 0, ..., 0, -1, ... is not a crossing either
- 0, 0, ..., 1, ... is not a crossing
- ..., 1, 0, 0, ..., 0 is not a crossing.
Thus, the output ought not to be just a single index for each crossing, but an interval of indexes. E.g., for {-2,-1,0,1,2,3,4,3,1,-2,-4,8,9,10} the output should be the set of ranges {2,4}, {9,10}, and {11,12}. From those you can select a unique value for the crossing if you must. (The mid-range of each would be a good choice, giving {3, 9.5, 11.5} instead of {3, 10, 12}.)
The procedure to find these intervals is not difficult or inefficient, but it might seem a little tricky, so the following code breaks it into simple steps and saves each step for inspection.
zeroCrossings[l_List] := Module[{t, u, v},
t = {Sign[l], Range[Length[l]]} // Transpose; (* List of -1, 0, 1 only *)
u = Select[t, First[#] != 0 &]; (* Ignore zeros *)
v = SplitBy[u, First]; (* Group into runs of + and - values *)
{Most[Max[#[[All, 2]]] & /@ v], Rest[Min[#[[All, 2]]] & /@ v]} // Transpose
]
Example
zeroCrossings[l = {0, -1, 1, -2, 0, 0, -1, 0, 0, 0, 1, 0, 1, 0, 2, -1, -3, 0, 0}]
{{2, 3}, {3, 4}, {7, 11}, {15, 16}}
This approach has a laudable symmetry: when the list is presented in reverse, we obtain exactly the same set of zero crossings (which is not the case for the example in the question):
Reverse /@ Reverse[Map[Length[l] + 1 - # &, zeroCrossings[Reverse[l]], {2}]]
{{2, 3}, {3, 4}, {7, 11}, {15, 16}}
Comments
Post a Comment