【问题标题】:Compound assignment operators in Python's Numpy libraryPython Numpy 库中的复合赋值运算符
【发布时间】:2012-06-15 14:12:03
【问题描述】:

Python 的 numpy 库对精美索引的“矢量化”有时会产生意想不到的结果。例如:

import numpy
a = numpy.zeros((1000,4), dtype='uint32')
b = numpy.zeros((1000,4), dtype='uint32')
i = numpy.random.random_integers(0,999,1000)
j = numpy.random.random_integers(0,3,1000)

a[i,j] += 1
for k in xrange(1000):
    b[i[k],j[k]] += 1

在数组 'a' 和 'b' 中给出不同的结果(即元组 (i,j) 的出现在 'a' 中显示为 1 而与重复无关,而在 'b' 中计算重复)。这很容易验证如下:

numpy.sum(a)
883
numpy.sum(b)
1000

同样值得注意的是,花哨的索引版本几乎比 for 循环快两个数量级。我的问题是:“numpy 是否有一种有效的方法来计算重复计数,如在提供的示例中使用 for 循环实现的那样?”

【问题讨论】:

  • 语义不同:for循环按顺序工作(重复索引的值递增多次),但a += 1等价于a = a + 1(重复索引递增一次)。
  • 我知道语义上存在差异(我不应该在我的原始帖子中说“意外”),但是有没有一种有效的方法来计算 for 循环语义(即计算 ( i,j) 元组) 使用 numPy?
  • 懒惰的选择是使用 Cython。它可能比基于函数的 numpy 版本更快、更易读。尽管后来的力量以更笼统的方式考虑操作(这可能是一件好事),但最终的解决方案可能更具可扩展性,例如stackoverflow.com/questions/4962606/…

标签: python numpy


【解决方案1】:

这应该做你想做的:

np.bincount(np.ravel_multi_index((i, j), (1000, 4)), minlength=4000).reshape(1000, 4)

作为细分,ravel_multi_indexij 指定的索引对转换为整数索引到C 展平数组中; bincount 计算每个值 0..4000 在该索引列表中出现的次数; reshape 将 C 平面化数组转换回二维数组。

在性能方面,我测量它比“b”快 200 倍,比“a”快 5 倍;您的里程可能会有所不同。

由于您需要将计数写入现有数组a,请尝试以下操作:

u, inv = np.unique(np.ravel_multi_index((i, j), (1000, 4)), return_inverse=True)
a.flat[u] += np.bincount(inv)

我让第二种方法比“a”慢一点(2x),这并不奇怪,因为unique 阶段会很慢。

【讨论】:

  • 我考虑过使用 bincount()。但是,我的数组实际上比我提供的示例大得多 (>300*106)。并且更新 (i,j) 对的数量相对较少(大约 20*106),重复次数较多。恐怕bincount方式会占用太多内存。
  • @user1451766 - bincount 使用的内存不会超过您在原始帖子中构建 a = numpy.zeros(...) 所需的内存。
  • 在我的例子中,'a' 是一个 memmap() 文件。花式索引或 for 循环方法都不需要这么大的中间内存缓冲区。
  • 但是,您的 ravel_multi_indexunique( ,return_inverse=True) 结合使用可能会成功!
  • 我添加了另一个方法,是你想要的吗?
猜你喜欢
  • 2019-10-06
  • 2011-11-16
  • 1970-01-01
  • 2020-12-01
  • 1970-01-01
  • 2017-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多