要矢量化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 y
是np.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=1000
,alt
爲約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
當orig
的for-loop
所需的迭代次數增加時 - 即bins
增加時,alt
高於orig
的速度優勢將會增加。
爲了(可能)對這個計算進行向量化,我們需要看到f的定義。 – unutbu
@unutbu f非常複雜且各不相同,但它總是隻包含Python數學函數/運算符:pow,min,max,floor,<, >,log。如果我給出一個明確的例子,它會有幫助嗎? – knzhou