如何在numpy中进行分散/聚集操作

时间:2010-09-29 03:28:35

标签: python numpy

假设我有数组:

a = array((1,2,3,4,5))
indices = array((1,1,1,1))

我执行操作:

a[indices] += 1

结果是

array([1, 3, 3, 4, 5])

换句话说,忽略indices中的重复项

如果我希望副本不被忽略,导致:

array([1, 6, 3, 4, 5])

我该怎么做?

上面的例子有些微不足道,接下来正是我要做的事情:

def inflate(self,pressure):
    faceforces = pressure * cross(self.verts[self.faces[:,1]]-self.verts[self.faces[:,0]], self.verts[self.faces[:,2]]-self.verts[self.faces[:,0]])
    self.verts[self.faces[:,0]] += faceforces
    self.verts[self.faces[:,1]] += faceforces
    self.verts[self.faces[:,2]] += faceforces

def constrain_lengths(self):
    vectors = self.verts[self.constraints[:,1]] - self.verts[self.constraints[:,0]]
    lengths = sqrt(sum(square(vectors), axis=1))
    correction = 0.5 * (vectors.T * (1 - (self.restlengths / lengths))).T
    self.verts[self.constraints[:,0]] += correction
    self.verts[self.constraints[:,1]] -= correction

def compute_normals(self):
    facenormals = cross(self.verts[self.faces[:,1]]-self.verts[self.faces[:,0]], self.verts[self.faces[:,2]]-self.verts[self.faces[:,0]])
    self.normals.fill(0)
    self.normals[self.faces[:,0]] += facenormals
    self.normals[self.faces[:,1]] += facenormals
    self.normals[self.faces[:,2]] += facenormals
    lengths = sqrt(sum(square(self.normals), axis=1))
    self.normals = (self.normals.T / lengths).T

由于在我的索引分配操作中忽略了重复项,我得到了一些非常错误的结果。

3 个答案:

答案 0 :(得分:4)

numpy的{​​{1}}函数是一个分散操作。

histogram

您可能需要注意,因为如果a += histogram(indices, bins=a.size, range=(0, a.size))[0]包含整数,则小的舍入错误可能导致值以错误的桶结尾。在这种情况下使用:

indices

将每个索引放入每个bin的中心。

更新:这有效。但我建议使用基于a += histogram(indices, bins=a.size, range=(-0.5, a.size-0.5))[0]的@Eelco Hoogendoorn答案。

答案 1 :(得分:2)

参加聚会的时间稍晚,但看到这项操作需要多少,以及它似乎仍然不是标准numpy的一部分,我把解决方案放在这里供参考:

def scatter(rowidx, vals, target):
    """compute target[rowidx] += vals, allowing for repeated values in rowidx"""
    rowidx = np.ravel(rowidx)
    vals   = np.ravel(vals)
    cols   = len(vals)
    data   = np.ones(cols)
    colidx = np.arange(cols)
    rows   = len(target)
    from scipy.sparse import coo_matrix
    M = coo_matrix((data,(rowidx,colidx)), shape=(rows, cols))
    target += M*vals
def gather(idx, vals):
    """for symmetry with scatter"""
    return vals[idx]

对于初学者来说,numpy中的自定义C例程可以轻松地快两倍,从而消除了多余的分配和乘法,但它在性能方面与Python中的循环相比有所不同。

除了性能方面的考虑之外,它在风格上更符合其他numpy-vectorized代码使用分散操作,而不是在代码中混合一些for循环。

编辑:

好的,忘了以上。截至最新的1.8版本,现在直接支持分散操作,以最佳效率实现numpy。

def scatter(idx, vals, target):
    """target[idx] += vals, but allowing for repeats in idx"""
    np.add.at(target, idx.ravel(), vals.ravel())

答案 2 :(得分:1)

我不知道如何做到这一点的速度要快于:

for face in self.faces[:,0]:
    self.verts[face] += faceforces

您还可以将self.faces变为3个字典的数组,其中键对应于面,值与需要添加的次数相对应。然后你会得到如下代码:

for face in self.faces[0]:
    self.verts[face] += self.faces[0][face]*faceforces

可能更快。我希望有人提出一个更好的方法,因为我想在今天早些时候尝试帮助某人加速他们的代码时这样做。