2016-07-26 63 views
1

我正在與numpy的Python和我有一個環路,剝離下來,看起來像這樣:向量化與可變數組索引

result = np.zeros(bins) 
for i in xrange(bins): 
    result[f(i)] += source[i] 

在這裏,兩者結果和源是numpy的陣列,而f是一組稍微複雜的算術運算。例如,f的一個簡化示例可能看起來像

f = lambda x: min(int(width*pow(x, 3)), bins-1) 

儘管f在其論證中通常不是單調的。

這個循環目前是我的程序中的瓶頸。我設法矢量化了其他所有內容,但是我目前在這裏如何做到這一點。這個循環如何被矢量化?

+1

爲了(可能)對這個計算進行向量化,我們需要看到f的定義。 – unutbu

+0

@unutbu f非常複雜且各不相同,但它總是隻包含Python數學函數/運算符:pow,min,max,floor,<, >,log。如果我給出一個明確的例子,它會有幫助嗎? – knzhou

回答

3

要矢量化f 主要想法是用基於numpy向量的函數替換標量運算。 例如,如果原來我們有

def f(x): 
    return min(int(width*pow(x, 3)), bins-1) 

那麼我們可以改用

def fvec(x): 
    return np.minimum((width*np.power(x, 3)).astype(int), bins-1) 

有一些Python標量函數和NumPy的 矢量功能之間的自然對應:

| pow | np.power | 
| min | np.minimum | 
| max | np.maximum | 
| floor | np.floor | 
| log | np.log  | 
| <  | np.less | 
| >  | np.greater | 

矢量化函數接受一組輸入並返回相同形狀的數組。 但是,還有其他的構造可能不那麼明顯。例如 矢量化等效的x if condition else ynp.where(condition, x, y)

不幸的是,一般來說沒有簡單的捷徑。將標量函數轉換爲矢量化函數可能需要NumPy函數中的任何一個,以及NumPy概念,例如廣播和高級索引 。


例如,它是在這一點上誘人與integer-array indexed assignment取代

for i in range(bins): 
    result[f(i)] += source[i] 

result[fvec(np.arange(bins))] += source 

但是這會產生一個不正確的結果,如果已經fvec(np.arange(bins))重複值。代替使用 np.bincount因爲這正確地累加多個source值時fvec(np.arange(bins))表示相同的箱櫃:

result = np.bincount(fvec(np.arange(bins)), weights=source, minlength=bins) 

import numpy as np 
import pandas as pd 

bins = 1000 
width = 1.5 
source = np.random.random(bins) 

def fvec(x): 
    return np.minimum((width*np.power(x, 3)).astype(int), bins-1) 

def f(x): 
    return min(int(width*pow(x, 3)), bins-1) 

def orig(): 
    result = np.zeros(bins) 
    for i in range(bins): 
     result[f(i)] += source[i] 
    return result 

def alt(): 
    result = np.bincount(fvec(np.arange(bins)), weights=source, minlength=bins) 
    return result 

assert np.allclose(orig(), alt()) 

對於上面的例子與bins=1000alt爲約62X比orig更快(上我的機器):

In [194]: %timeit orig() 
1000 loops, best of 3: 1.37 ms per loop 

In [195]: %timeit alt() 
10000 loops, best of 3: 21.8 µs per loop 

origfor-loop所需的迭代次數增加時 - 即bins增加時,alt高於orig的速度優勢將會增加。

+0

這絕對完美,謝謝! – knzhou

+0

我剛剛實現了這一點,並獲得了100倍的加速。好東西! – knzhou