2015-04-05 32 views
0

我想在我的算法中編寫一段嵌套循環,並且遇到一些由於嵌套循環而導致整個算法花費太長時間的問題。我對Python很陌生(正如你可能從我的下面非專業代碼:()中找到的,希望有人能指導我加速我的代碼的方法!如何加快Python中的嵌套循環

整個算法用於多1500 * 6400陣列,當通過整個數組時,應用一個小的上下文分析,上下文分析以動態分配的窗口大小的方式執行,窗口大小可以從11 * 11到31 * 31,直到採樣窗口內的有效值足夠大爲下一輪計算,例如象下面這樣:

def ContextualWindows (arrb4,arrb5,pfire): 
####arrb4,arrb5,pfire are 31*31 sampling windows from large 1500*6400 numpy array 
    i=5 
    while i in range (5,16): 
     arrb4back=arrb4[15-i:16+i,15-i:16+i] 
     ## only output the array data when it is 'large' enough 
     ## to have enough good quality data to do calculation  
     if np.ma.count(arrb4back)>=min(10,0.25*i*i): 
      arrb5back=arrb5[15-i:16+i,15-i:16+i] 
      pfireback=pfire[15-i:16+i,15-i:16+i] 
      canfire=0 
      i=20 
     else: 
      i=i+1 

###unknown pixel: background condition could not be characterized 
    if i!=20: 
     canfire=1 
     arrb5back=arrb5 
     pfireback=pfire 
     arrb4back=arrb4 
    return (arrb4back,arrb5back,pfireback,canfire) 

然後這個動態窗口將被進給到下一輪試驗中,例如:

b4backave=np.mean(arrb4Windows) 
b4backdev=np.std(arrb4Windows) 
if b4>b4backave+3.5*b4backdev: 
    firetest=True 

要運行整個代碼到我的多1500 * 6400 numpy陣列,它花了半小時甚至更長的時間。想知道是否有人知道如何處理它?我應該努力的一部分的一般想法將會非常有幫助!

非常感謝!

+2

可能會更好[codereview](http://codereview.stackexchange.com/)? – hd1 2015-04-05 16:24:18

+0

不是複雜性的改變,但我會使用'while 5 <= i <16' – 2015-04-05 16:40:49

+0

你也可以改變它爲for循環和break,else else i = 20然後顛倒測試...有沒有嵌套循環,只是很多切片,numpy應該創建視圖,所以不應該那麼慢。 – AChampion 2015-04-05 16:44:59

回答

1

避免while循環,如果速度是一個問題。該循環適用於for循環,因爲開始和結束都是固定的。此外,你的代碼做了很多複製,這並不是必須的。重寫的功能:

def ContextualWindows (arrb4,arrb5,pfire): 
    ''' arrb4,arrb5,pfire are 31*31 sampling windows from 
     large 1500*6400 numpy array ''' 

    for i in range (5, 16): 
     lo = 15 - i # 10..0 
     hi = 16 + i # 21..31 
     # only output the array data when it is 'large' enough 
     # to have enough good quality data to do calculation 
     if np.ma.count(arrb4[lo:hi, lo:hi]) >= min(10, 0.25*i*i): 
      return (arrb4[lo:hi, lo:hi], arrb5[lo:hi, lo:hi], pfire[lo:hi, lo:hi], 0) 
    else: # unknown pixel: background condition could not be characterized 
     return (arrb4, arrb5, pfire, 1) 

爲清楚起見,我用從PEP 8風格指南(如擴展註釋,註釋字符的數量,各地運營空間等)。複製窗口arrb4會在這裏出現兩次,但只有滿足條件時纔會發生,並且每個函數調用僅發生一次。只有在for -loop運行結束時纔會執行else子句。我們甚至不需要循環中的break,因爲我們完全退出該函數。
讓我們知道這是否會加快代碼的速度。我不認爲它會很多,但是不管怎麼說,代碼都不多。

+0

終點不固定。他從5開始,但是可以在16之前退出。有休息的for-loop應該可以工作,但我認爲這不會在速度上產生太大的影響。 – hpaulj 2015-04-05 20:09:48

+0

我的建議在'if ... then'語句中也包含一個'break',只是它從函數完全返回。如果OP在條件滿足時就想過早退出,那麼它就無法工作。所以循環可能會運行到結束,或者可能會在之前退出,就像在原始代碼中一樣。在我看來,這個流程適用於帶'break'的for循環,而不是'while'循環。避免複製數組的部分可能會比​​這更有效果,授予。 – user1016274 2015-04-05 20:17:17

+0

在我的測試中,1次迭代需要50us,全部10,500us。如果可能的話儘早打破是一件好事。因爲break有更清晰的語法,但速度沒有太大差別。 – hpaulj 2015-04-05 20:19:00

0

我用ContextualWindows及其變種運行了一些時間測試。一個i步大約需要50微秒,所有十​​個約500

這個簡單的迭代花費大約在同一時間:

[np.ma.count(arrb4[15-i:16+i,15-i:16+i]) for i in range(5,16)] 

迭代機制,與「複製」陣列的時間次要部分。如有可能,numpy正在製作視圖,而不是副本。

我會專注於最小化這些count步驟的數量,或者加快count。現在

In [167]: timeit [np.ma.count(arrb4[15-i:16+i,15-i:16+i]) for i in range(5,6)] 
10000 loops, best of 3: 43.9 us per loop 

爲10個步驟:


在這些窗口比較各種業務時間:

第一時間爲1步

In [139]: timeit [arrb4[15-i:16+i,15-i:16+i].shape for i in range(5,16)] 
10000 loops, best of 3: 33.7 us per loop 

In [140]: timeit [np.sum(arrb4[15-i:16+i,15-i:16+i]>500) for i in range(5,16)] 
1000 loops, best of 3: 390 us per loop 

In [141]: timeit [np.ma.count(arrb4[15-i:16+i,15-i:16+i]) for i in range(5,16)] 
1000 loops, best of 3: 464 us per loop 

簡單的索引不花費很多時間,但測試條件需要更多。

cumsum有時用於加快滑動窗口的總和。而不是在每個窗口上計算總和(或平均值),然後計算cumsum,然後使用窗口前端和末端之間的差異。

嘗試類似的東西,但在2D - cumsum在兩個維度上,隨後對角相對的角部之間的差別:

In [164]: %%timeit 
    .....: cA4=np.cumsum(np.cumsum(arrb4,0),1) 
    .....: [cA4[15-i,15-i]-cA4[15+i,15+i] for i in range(5,16)] 
    .....: 
10000 loops, best of 3: 43.1 us per loop 

這幾乎10倍比(幾乎)等效sum更快。價值觀並不完全匹配,但時機表明這可能值得提煉。