Подтвердить что ты не робот

Обработка повторяющихся индексов в заданиях NumPy

Я устанавливаю значения нескольких элементов в 2D-массиве, однако мои данные иногда содержат несколько значений для данного индекса.

Кажется, что значение "позже" всегда назначается (см. примеры ниже), но гарантируется ли это поведение или есть вероятность, что я получу противоречивые результаты? Как узнать, что я могу интерпретировать "позже" так, как хотелось бы в векторизованном назначении?

то есть. в моем первом примере a определенно всегда будет содержать 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]
4b9b3361

Ответ 1

В NumPy 1.9 и более поздних версиях это, в общем, не будет корректно определено.

Текущая реализация выполняет итерацию по всем (широковещательным) фантазийным индексам (и массиву присваивания) одновременно с использованием отдельных итераторов, и эти итераторы используют C-порядок. Другими словами, в настоящее время, да, вы можете. Так как вы, возможно, захотите узнать его более точно. Если вы сравниваете mapping.c в NumPy, который обрабатывает эти вещи, вы увидите, что он использует PyArray_ITER_NEXT, который документирован, чтобы быть в C -порядок.

В будущем я бы покрасил картину по-другому. Я думаю, было бы неплохо повторить все индексы + массив присваивания, используя новый итератор. Если это будет сделано, тогда порядок может быть открыт для итератора, чтобы решить самый быстрый способ. Если вы держите его открытым для итератора, трудно сказать, что произойдет, но вы не можете быть уверены, что ваш пример работает (возможно, 1-й случай, который вы все еще можете, но...).

Итак, насколько я могу судить, он работает в настоящее время, но он недокументирован (для всех, кого я знаю), поэтому, если вы действительно думаете, что это должно быть обеспечено, вам нужно будет лоббировать его и лучше написать некоторые тесты, чтобы сделать уверен, что это может быть гарантировано. Потому что, по крайней мере, у меня возникает соблазн сказать: если это ускоряет работу, нет причин для обеспечения C-ордера, но, конечно, возможно, есть какая-то серьезная причина, скрытая где-то...

Реальный вопрос: зачем вам это все равно?;)

Ответ 2

Я знаю, что это было удовлетворительно отреагировано, но я хотел бы упомянуть, что он задокументирован как "последнее значение" (возможно, неформально) в Ориентировочный Учебное пособие по Numpy под Индексация с массивами индексов:

Однако, когда список индексов содержит повторы, назначение выполняется несколько раз, оставляя последнее значение:

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

Это достаточно разумно, но будьте осторожны, если вы хотите использовать Python + = construct, поскольку он может не делать то, что вы ожидаете:

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

Несмотря на то, что 0 встречается дважды в списке индексов, 0-й элемент только увеличивается один раз. Это связано с тем, что Python требует, чтобы a+=1 был эквивалентен a=a+1.

Ответ 3

Я не отвечаю на ваш вопрос напрямую, я просто хотел бы подчеркнуть, что , даже если вы можете полагаться на то, что это согласованное поведение, вам лучше не делать.

Рассмотрим:

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

В этот момент разумно предположить, что a.sum() есть a предыдущая сумма + x.sum()?

assert a.sum() == x.sum()
--> AssertionError 

a
= array([ 3.,  0.,  0.,  0.])

В вашем случае при назначении массиву с использованием повторяющихся индексов результат интуитивно понятен: назначение в один и тот же индекс происходит несколько раз, поэтому только последнее присваивание "прилипает" (оно перезаписывает предыдущие).

Но в этом примере это не так. Это уже не интуитивно. Если бы это было так, добавление на месте происходило бы несколько раз, потому что добавление является кумулятивным по своей природе.

Итак, по-другому, вы рискуете попасть в эту ловушку:

  • вы начинаете работать с повторяющимися индексами
  • вы видите, что все хорошо, поведение точно так, как вы ожидаете
  • вы перестаете обращать внимание на тот важный факт, что ваши операции включают в себя повторяющиеся индексы. В конце концов, это не имеет значения, не так ли?
  • вы начинаете использовать одни и те же индексы в разных контекстах, например. как указано выше
  • глубоко сожалеет:)

Итак, цитируя @seberg:

Реальный вопрос: зачем вам это все равно?;)

Ответ 4

Я нашел способ с numpy для выполнения этой операции это явно не оптимально, но быстрее, чем цикл (с питоном для цикла)

с: 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]

что хорошо!