Numpy中的矢量化 - 广播

时间:2014-03-21 17:57:10

标签: python arrays numpy vectorization

我在python中有一个带有以下元素的代码:

我有一个intensities向量,它是这样的:

array([ 1142.,  1192.,  1048., ...,    29.,    18.,    35.])

我还有一个x向量,如下所示:

array([   0,    1,    1, ..., 1060, 1060, 1061])

然后,我有for循环,我填充另一个向量radialDistribution,如下所示:

for i in range(1000):
    radialDistribution[i] = sum(intensities[np.where(x == i)]) / len(np.where(x == i)[0])

问题在于它需要20秒才能完成它...因此我想对它进行矢量化。但我对Numpy的广播很新,并没有找到那么多......所以我需要你的帮助。

我尝试了这个,但没有工作:

i= np.ogrid[:1000]
intensities[i] = sum(sortedIntensities1D[np.where(sortedDists1D == i)]) / len(np.where(sortedDists1D == i)[0])

你能帮我告诉我在哪里可以学习Numpy的矢量化程序吗?

提前感谢您的宝贵帮助!

4 个答案:

答案 0 :(得分:5)

如果您的x向量具有从0开始的连续整数,那么您可以执行以下操作:

radialDistribution = np.bincount(x, weights=intensities) / np.bincount(x)

答案 1 :(得分:2)

Here是我在numpy中实现的group_by功能。它在概念上类似于熊猫解决方案;除了这不需要大熊猫,我认为它应该成为numpy核心的一部分。

使用此功能,您的代码将如下所示:

radialDistribution = group_by(x).mean(intensities)

并将在notime中完成。

另请参阅最后定义的test_radial函数,它可能更接近您的endgoal。

答案 2 :(得分:1)

这是一种使用广播的方法:

# arrays need to be at least 2D for broadcasting
x = np.atleast_2d(x)

# create vector of indices
i = np.atleast_2d(np.arange(x.size))

# do the vectorized calculation
bool_eq = (x == i.T)
totals = np.sum(np.where(bool_eq, intensities, 0), axis=1)
rD = totals / np.sum(bool_eq, axis=1)

这使用广播两次:在操作x == i.T和对np.where的调用中。不幸的是,上面的代码非常慢,甚至比原始代码慢。这里的主要瓶颈是np.where,在这种情况下我们可以通过获取布尔数组和强度(也通过广播)的乘积来加速:

totals = np.sum(bool_eq*intensities, axis=1)

这与矩阵矢量产品基本相同,所以我们可以写:

totals = np.dot(intensities, bool_eq.T)

最终结果是比原始代码更快的代码(至少在中间数组的内存使用成为限制因素之前),但是你可能最好采用迭代方法,如其他答案之一所示

编辑:使用np.einsum的速度更快(在我的试用版中):

totals = np.einsum('ij,j', bool_eq, intensities)

答案 3 :(得分:0)

基于https://stackoverflow.com/a/22265803/901925中的itertools.groupby解决方案,这是一个适用于2个小数组的解决方案。

import numpy as np
import itertools
intensities = np.arange(12,dtype=float)
x=np.array([1,0,1,2,2,1,0,0,1,2,1,0]) # general, not sorted or consecutive

首先是一个bincount解决方案,根据非连续值进行调整

# using bincount
# if 'x' are not consecutive
J=np.bincount(x)>0
print np.bincount(x,weights=intensities)[J]/np.bincount(x)[J]

现在是groupby解决方案

# using groupby;
# sort if need
I=np.argsort(x)
x=x[I]
intensities=intensities[I]

# make a record array for use by groupby
xi=np.zeros(shape=x.shape, dtype=[('intensities',float),('x',int)])
xi['intensities']=intensities
xi['x']=x

g=itertools.groupby(xi, lambda z:z['x'])
xx=np.array([np.array([z[0] for z in y[1]]).mean() for y in g])
print xx

这是一个紧凑的numpy解决方案,使用return_indexnp.unique的{​​{1}}选项。 np.split应该排序。我对大数组的速度并不乐观,因为除了理解之外,xunique还会有迭代。

split