在NumPy分配中处理重复的索引

我正在设置二维数组中的多个元素的值,但是我的数据有时包含给定索引的多个值。

看来,“后来”的价值总是分配(见下面的例子),但是这种行为是保证还是有机会我会得到不一致的结果? 我怎么知道我可以用我想要的vector化作业的方式来解释“以后”?

即在我的第一个例子中肯定会总是包含4 ,在第二个例子中,它会打印values[0]

很简单的例子:

 import numpy as np indices = np.zeros(5,dtype=np.int) a[indices] = np.arange(5) a # array([4]) 

另一个例子

 import numpy as np grid = np.zeros((1000, 800)) # generate indices and values xs = np.random.randint(0, grid.shape[0], 100) ys = np.random.randint(0, grid.shape[1], 100) values = np.random.rand(100) # make sure we have a duplicate index print values[0], values[5] xs[0] = xs[5] ys[0] = ys[5] grid[xs, ys] = values print "output value is", grid[xs[0], ys[0]] # always prints value of values[5] 

在NumPy 1.9及之后的版本中,这一般不会被很好地定义。

当前实现使用单独的迭代器同时迭代所有(广播的)幻想索引(和赋值数组),并且这些迭代器都使用C顺序。 换句话说,目前,是的,你可以。 既然你可能想更确切地知道它。 如果你比较NumPy中的mapping.c处理这些事情,你会看到它使用PyArray_ITER_NEXT ,它被logging为C顺序。

为了将来,我会以不同的方式绘制图片。 我认为用新的迭代器迭代所有的索引+赋值数组是很好的。 如果这样做了,那么顺序可以保持打开,迭代器决定最快的方式。 如果你保持对迭代器开放,很难说会发生什么,但是你不能确定你的例子的工作原理(可能是你仍然可以的一维情况,但是…)。

所以,据我目前可以告诉它的工作,但它是无证的(我知道),所以如果你真的认为这应该得到保证,你需要游说,最好写一些testing,以确保它可以得到保证。 因为至less有人试图说:如果它让事情变得更快,没有理由确保C顺序,但当然也许有隐藏在某个地方的一个很好的理由…

这里真正的问题是:为什么你要这样呢? ;)

我知道这已经得到了令人满意的回答,但是我想提一下,在“ 指数数组索引”下的“ Numpy Tutorial ”中,它被logging为“ 最后一个值 ”(可能是非正式的)

但是,当索引列表包含重复时,分配会执行好几次,而留下最后一个值:

 >>> a = arange(5) >>> a[[0,0,2]]=[1,2,3] >>> a array([2, 1, 3, 3, 4]) 

这足够合理,但要小心如果你想使用Python的+ =构造,因为它可能不会做你期望的:

 >>> a = arange(5) >>> a[[0,0,2]]+=1 >>> a array([1, 1, 3, 3, 4]) 

即使0在索引列表中出现两次,第0个元素也只会增加一次。 这是因为Python需要a+=1才能相当于a=a+1

我不直接回答你的问题,我只想指出, 即使你可以依靠这种行为是一致的,你最好不要。

考虑:

 a = np.zeros(4) x = np.arange(4) indices = np.zeros(4,dtype=np.int) a[indices] += x 

在这一点上,假设a.sum()是前一个sum + x.sum()是否x.sum()

 assert a.sum() == x.sum() --> AssertionError a = array([ 3., 0., 0., 0.]) 

在你的情况下,当使用重复索引分配给一个数组时,结果是直观的:对同一个索引的赋值多次发生,因此只有最后一个赋值“sticks”(覆盖前面的那个)。

但在这个例子中情况并非如此。 它不再直观。 如果是这样的话,原地增加就会多次发生,因为增加的性质是累积的。

换句话说,你冒着陷入这个陷阱的风险:

  • 你开始使用重复的索引
  • 你看到一切都很好,行为完全如你所料
  • 你停止关注你的操作涉及重复索引的关键事实。 毕竟,这没有什么区别,是吗?
  • 你开始在不同的上下文中使用相同的索引,例如上面那样
  • 深深的遗憾:)

所以,引用@seberg:

这里真正的问题是:为什么你要这样呢? ;)

我发现一个与numpy的方式来做这个操作,这显然不是最优的,但它比循环更快(用Python for循环)

与:numpy.bincount

 size = 5 a = np.arange(size) index = [0,0,2] values = [1,2,3] a[index] += values a [2 1 5 3 4] 

女巫是不正确的,但:

 size = 5 a = np.arange(size) index = [0,0,2] values = [1,2,3] result = np.bincount(index, values, size) a += result a [3 1 5 3 4] 

这很好!