在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]
这很好!