Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Efficiently detect sign-changes in python

I want to do exactly what this guy did:

Python - count sign changes

However I need to optimize it to run super fast. In brief I want to take a time series and tell every time it crosses crosses zero (changes sign). I want to record the time in between zero crossings. Since this is real data (32 bit float) I doubt I'll every have a number which is exactly zero, so that is not important. I currently have a timing program in place so I'll time your results to see who wins.

My solution gives (micro seconds):

open data       8384 sign data       8123 zcd data        415466 

As you can see the zero-crossing detector is the slow part. Here's my code.

import numpy, datetime  class timer():     def __init__(self):         self.t0 = datetime.datetime.now()         self.t = datetime.datetime.now()     def __call__(self,text='unknown'):         print text,'\t',(datetime.datetime.now()-self.t).microseconds         self.t=datetime.datetime.now()  def zcd(data,t):     sign_array=numpy.sign(data)     t('sign data')     out=[]     current = sign_array[0]     count=0     for i in sign_array[1:]:         if i!=current:             out.append(count)             current=i             count=0         else: count+=1     t('zcd data')     return out  def main():     t = timer()     data = numpy.fromfile('deci.dat',dtype=numpy.float32)     t('open data')     zcd(data,t)  if __name__=='__main__':     main() 
like image 580
chriscauley Avatar asked Oct 01 '10 20:10

chriscauley


2 Answers

What about:

import numpy a = [1, 2, 1, 1, -3, -4, 7, 8, 9, 10, -2, 1, -3, 5, 6, 7, -10] zero_crossings = numpy.where(numpy.diff(numpy.sign(a)))[0] 

Output:

> zero_crossings array([ 3,  5,  9, 10, 11, 12, 15]) 

I.e., zero_crossings will contain the indices of elements before which a zero crossing occurs. If you want the elements after, just add 1 to that array.

like image 147
Jim Brissom Avatar answered Oct 03 '22 02:10

Jim Brissom


As remarked by Jay Borseth the accepted answer does not handle arrays containing 0 correctly.

I propose using:

import numpy as np a = np.array([-2, -1, 0, 1, 2]) zero_crossings = np.where(np.diff(np.signbit(a)))[0] print(zero_crossings) # output: [1] 

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.

However there is one drawback, maybe: If your input array starts and stops with zeros, it will find a zero crossing at the beginning, but not at the end...

import numpy as np a = np.array([0, -2, -1, 0, 1, 2, 0]) zero_crossings = np.where(np.diff(np.signbit(a)))[0] print(zero_crossings) # output: [0 2] 
like image 37
Dominik Neise Avatar answered Oct 03 '22 02:10

Dominik Neise