要矢量化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