2016-07-26 118 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