2010-10-01 91 views
19

登錄改變我想做這個傢伙做了什麼:有效地檢測出蟒蛇

Python - count sign changes

但是我需要優化它運行超級快。簡而言之,我想要抽取一個時間序列,並告訴它每次穿過零(改變符號)。我想記錄零交叉點之間的時間。由於這是真實的數據(32位浮點數),我懷疑每個數字都是零,所以這並不重要。我目前有一個計時程序,所以我會計時結果看看誰贏了。

我的解決辦法給出(微秒):

open data  8384 
sign data  8123 
zcd data  415466 

正如你可以看到過零檢測器是慢的部分。這是我的代碼。

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() 
+2

有一個'timeit'模塊,你知道嗎? :) – 2010-10-01 21:04:15

+0

有趣......我更喜歡我的,因爲它可以放在整個功能。你可以放下一個t()每行幾行,並快速找到瓶頸。如果我只想要我的功能,我會使用Linux'$ time python zcd.py' – chriscauley 2010-10-01 21:10:44

+0

我猜'行'時間('符號數據')'是爲了'噸('符號數據')' 。是嗎? – 2010-10-01 21:23:51

回答

46

什麼:

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] 

輸出:

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

即zero_crossings將包含元素的索引其過零點發生之後。如果你想要之前的元素,只需將1添加到該數組。

+1

我認爲你有它倒退; 'zero_crossings'包含元素的頂點_before其中_過零發生,如果你想元素_after_你加1到數組。否則,優秀,簡潔的答案! – staticfloat 2012-10-04 21:51:06

+10

當數組中有零時,這不起作用。它會檢測他們兩次!例如:'a = [2,1,0,-1,2]'會給出數組([1,2,3])' – YuppieNetworking 2014-06-05 13:08:41

3

你想要它嗎?或者你想盡可能快地做到這一點?

時間很簡單。運行它數十億次,秒錶,然後除以十億。

爲了讓它儘可能快,您需要做的是找出需要花費的時間,並且您可以以更好的方式做到。我使用1)隨機暫停技術,或2)單步技術。如果一個包含

+0

+1隨機停頓很大 – knitti 2010-10-01 21:37:32

+0

時間很簡單,運行速度足夠快您可以通過運行一次來​​獲得準確的時間。我希望腳本能夠快速運行,因爲它是半實時數據處理器的一部分。 – chriscauley 2010-10-01 23:55:38

+1

@dustynachos:FWIW,這是一個使用隨機暫停來獲得一系列加速超過40倍的加速計劃。 http://stackoverflow.com/questions/926266/performance-optimization-strategies-of-last-resort/927773#927773 – 2010-10-02 02:07:26

8

吉姆Brissom的答案失敗的值0:

import numpy 
a2 = [1, 2, 1, 1, 0, -3, -4, 7, 8, 9, 10, -2, 1, -3, 5, 6, 7, -10] 
zero_crossings2 = numpy.where(numpy.diff(numpy.sign(a2)))[0] 
print zero_crossings2 
print len(zero_crossings2) # should be 7 

輸出:

[ 3 4 6 10 11 12 13 16] 
8 

零交叉的數量應該是7,但由於符號()返回0,如果傳遞0時,1表示正值,-1表示負值,diff()會計算兩次包含零的轉換。

另一種可能是:

a3 = [1, 2, 1, 1, 0, -3, -4, 7, 8, 9, 10, 0, -2, 0, 0, 1, 0, -3, 0, 5, 6, 7, -10] 
s3= numpy.sign(a3) 
s3[s3==0] = -1  # replace zeros with -1 
zero_crossings3 = numpy.where(numpy.diff(s3))[0] 
print s3 
print zero_crossings3 
print len(zero_crossings3) # should be 7 

這給正確答案:

[ 3 6 10 14 15 18 21] 
7 
+0

謝謝 - 我只是遇到這個答案。我想知道是否有一種簡單的方法來了解零交叉的**符號**(高於0或低於0)?斜坡應該可能有所幫助。 – 2014-12-04 22:24:43

+1

這不考慮0之前和之後的元素具有相同符號的情況。 – skyork 2015-06-09 23:07:20

+0

而不是使用'numpy.sign',它返回-1,0或1的負數,零或正數,你應該使用'numpy.where(numpy.diff(a2> 0))[0]'。或者使用Dominik Neise的答案'np.signbit'。 – IceArdor 2015-11-19 00:08:18

7

另一種方法來計算過零點和擠壓短短數毫秒的時間出來的代碼是用nonzero並直接計算符號。假設你有一個data一維數組:

def crossings_nonzero_all(data): 
    pos = data > 0 
    npos = ~pos 
    return ((pos[:-1] & npos[1:]) | (npos[:-1] & pos[1:])).nonzero()[0] 

或者,如果只想計數過零點對於零交叉的特定方向(例如,從正到負),這是更快的:

def crossings_nonzero_pos2neg(data): 
    pos = data > 0 
    return (pos[:-1] & ~pos[1:]).nonzero()[0] 

在我的機器這些比where(diff(sign))方法(定時快一點爲含有20個循環,在所有的)40個交叉10000個正弦樣本的數組:

$ python -mtimeit 'crossings_where(data)' 
10000 loops, best of 3: 119 usec per loop 

$ python -mtimeit 'crossings_nonzero_all(data)' 
10000 loops, best of 3: 61.7 usec per loop 

$ python -mtimeit 'crossings_nonzero_pos2neg(data)' 
10000 loops, best of 3: 55.5 usec per loop 
20

正如Jay Borseth所言,接受的答案不能正確處理包含0的數組。

我建議使用:

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] 

由於a)使用numpy.signbit()比numpy.sign()更快一點,因爲它的實現更簡單,我想與B),正確處理在輸入數組中有零。

但是有一個缺點,也許:如果您的輸入數組,並開始以零停止,它還會找過零開頭,而不是在最後...

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] 
+1

嗯,那麼$ [ - 2,-1,0,-1,-2,0] $ ....沒有交叉只是觸摸,但答案。我猜,把零計爲正數也不是最終的解決方案。 – mikuszefski 2016-08-16 15:09:41

+0

@mikuszefski你是對的! '[1,2,0,-1,0,0,-1,2]'應該產生'2'過零點,而不是。 – 2016-11-25 16:32:01

1

我看到人們在他們的解決方案中使用差異很大,但xor似乎要快得多,結果對於bools來說是相同的(一個很好的指針可能也是使用diff給出了不推薦使用的警告...... :)) 這裏是一個例子:

positive = a2 > 0 
np.where(np.bitwise_xor(positive[1:], positive[:-1]))[0] 

它測量它的時間t Ø是一個繞半快DIFF我:)

如果你不關心邊緣情況下,它可能會更好地使用

positive = np.signbit(a2) 

但積極= A2> 0似乎更快(和更清潔)比signbit和檢查0(例如正數= np.bitwise_or(np.signbit(a2),np.logical_not(a2))較慢...)

0

另一種可能適合某些應用的方法是擴展表達式np.diff(np.sign(a))的評估。

如果我們比較這表達如何響應特定情況下:

  1. 上升交叉無零:np.diff(np.sign([-10, 10]))回報array([2])
  2. 瑞星,過零:np.diff(np.sign([-10, 0, 10]))回報array([1, 1])
  3. 落渡無零:np.diff(np.sign([10, -10]))回報array([-2])
  4. 落零與零:np.diff(np.sign([10, 0, -10]))返回array([-1, -1])

因此,我們必須在1和2,以評估np.diff(...)爲返回模式:

sdiff = np.diff(np.sign(a)) 
rising_1 = (sdiff == 2) 
rising_2 = (sdiff[:-1] == 1) & (sdiff[1:] == 1) 
rising_all = rising_1 
rising_all[1:] = rising_all[1:] | rising_2 

和案件3和4。:

falling_1 = (sdiff == -2) #the signs need to be the opposite 
falling_2 = (sdiff[:-1] == -1) & (sdiff[1:] == -1) 
falling_all = falling_1 
falling_all[1:] = falling_all[1:] | falling_2 

此之後,我們可以很容易地

indices_rising = np.where(rising_all)[0] 
indices_falling = np.where(falling_all)[0] 
indices_both = np.where(rising_all | falling_all)[0] 

這種方法找到了指數應該是合理的快,因爲它可以在不使用「慢」循環管理。

這結合了其他幾個答案的方法。