Numpy: группировка / группирование значений на основе ассоциаций

Forgive me for a vague title. I honestly don't know which title will suit this question. If you have a better title, let's change it so that it will be apt for the problem at hand.

Эта проблема.

Скажем result это 2D массив и values является одномерным массивом values содержит некоторые значения, связанные с каждым элементом в result, Отображение элемента в values в result хранится в x_mapping а также y_mapping, Положение в result могут быть связаны с разными значениями. Теперь я должен найти сумму значений, сгруппированных по ассоциациям.

Пример для лучшего разъяснения.

result массив:

[[0, 0],
[0, 0],
[0, 0],
[0, 0]]

values массив:

[ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.]

Примечание: здесь result а также values имеют одинаковое количество элементов. Но это может быть не так. Между размерами нет никакой связи.

x_mapping а также y_mapping есть отображения из 1D values в 2D result, Размеры x_mapping, y_mapping а также values будет таким же.

x_mapping - [0, 1, 0, 0, 0, 0, 0, 0]

y_mapping - [0, 3, 2, 2, 0, 3, 2, 1]

Здесь 1-е значение (values[0]) иметь х как 0 и у как 0(x_mapping[0] а также y_mappping[0]) и, следовательно, связано с result[0, 0], Если мы подсчитываем количество ассоциаций, то значение элемента в result[0,0] будет 2 как 1-е значение, а 5-е значение связано с result[0, 0], Если мы берем сумму, то result[0, 0] = value[0] + value[4] который 6

Текущее решение

# Initialisation. No connection with the solution.
result = np.zeros([4,2], dtype=np.int16)

values =  np.linspace(start=1, stop=8, num=8)
y_mapping = np.random.randint(low=0, high=values.shape[0], size=values.shape[0])
x_mapping = np.random.randint(low=0, high=values.shape[1], size=values.shape[0])
# Summing the values associated with x,y (current solution.)
for i in range(values.size):
    x = x_mapping[i]
    y = y_mapping[i]
    result[-y, x] = result[-y, x] + values[i]

result,

[[6, 0],
[ 6, 2],
[14, 0],
[ 8, 0]]

Неудачное решение; Но почему?

test_result = np.zeros_like(result)
test_result[-y_mapping, x_mapping] = test_result[-y_mapping, x_mapping] + values # solution

К моему удивлению элементы перезаписываются в test_result, Значения в test_result,

[[5, 0],
[6, 2],
[7, 0],
[8, 0]]

Вопрос

1. Почему во втором решении каждый элемент перезаписывается?

Как отметил @Divakar в комментарии в своем ответе, NumPy не присваивает накопленные / суммированные значения, когда индексы повторяются в test_result[-y_mapping, x_mapping] =, Он случайным образом присваивается из одного экземпляра.

2. Есть ли какой-нибудь способ Numpy сделать это? То есть без зацикливания? Я ищу некоторую оптимизацию скорости.

Подход № 2 в ответе @ Divakar дает мне хорошие результаты. Для 23315 ассоциаций, for цикл занял 50 мс, а подход № 1 - 1,85 мс. Победив все это, подход № 2 занял 668 мкс.

Примечание

Я использую Numpy версии 1.14.3 с Python 3.5.2 на процессоре i7.

2 ответа

Решение

Подход № 1

Самый интуитивный был бы с np.add.at для этих повторных индексов -

np.add.at(result, [-y_mapping, x_mapping], values)

Подход № 2

Нам нужно выполнить суммированные суммирования из-за возможной повторяющейся природы индексов x,y. Следовательно, другой способ может заключаться в использовании функции суммирования binum в NumPy: np.bincount и иметь такую ​​реализацию -

# Get linear index equivalents off the x and y indices into result array
m,n = result.shape
out_dtype = result.dtype
lidx = ((-y_mapping)%m)*n + x_mapping

# Get binned summations off values based on linear index as bins
binned_sums = np.bincount(lidx, values, minlength=m*n)

# Finally add into result array
result += binned_sums.astype(result.dtype).reshape(m,n)

Если вы всегда начинаете с массива нулей для result, последний шаг может быть сделан более производительным с -

result = binned_sums.astype(out_dtype).reshape(m,n)

Я думаю, ты должен был написать

y_mapping = np.random.randint(low=0, high=result.shape[0], size=values.shape[0])
x_mapping = np.random.randint(low=0, high=result.shape[1], size=values.shape[0])

С этим исправлением код работает для меня, как и ожидалось.

Другие вопросы по тегам