numpy - is there a way to cause multiple increments at arr[1, [0, 2, 0, 2, 0]] += 1

Viewed 42

Background

+1 occurs only once for each element although each indexed element is referenced multiple times.

a = np.arange(12).reshape((3, 4))
b = a.copy()
print(a)
---
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

a[1, [0, 2, 0, 2, 0]] += 1  #  a[1][0] referenced 3 times and a[1][2] 2 times
print(a-b)
---
[[0 0 0 0]
 [1 0 1 0]   # <--- +1 occurs only once for a[1][0] and a[1][2]
 [0 0 0 0]]

Question

Is there a way to create a sort of accumulative addition like below in a numpy native way?

# Result a
[[ 0  1  2  3]
 [ 7  5  8  7]    <--- +1 three times on a[1][0] and twice on a[1][2]
 [ 8  9 10 11]]

# a-b
[[0 0 0 0]
 [3 0 2 0]   # <--- +3 for a[1][0] and +2 for a[1][2]
 [0 0 0 0]]

One way can be reduce-like, similar to map/reduce word counting, but would like to know if there is a native numpy way.

enter image description here

2 Answers

What you're looking for is numpy.ufunc.at. Here is how you can use it in your case:

np.add.at(a, (1, [0, 2, 0, 2, 0]), 1)

print(a - b)
# [[0 0 0 0]
#  [3 0 2 0]
#  [0 0 0 0]]

From the docs:

Performs unbuffered in place operation on operand ‘a’ for elements specified by ‘indices’. For addition ufunc, this method is equivalent to a[indices] += b, except that results are accumulated for elements that are indexed more than once. For example, a[[0,0]] += 1 will only increment the first element once because of buffering, whereas add.at(a, [0,0], 1) will increment the first element twice.

As long as this accumulative addition can work along one axis at a time - you can utilize collections.Counter.

Consider the following:

import numpy as np
from collections import Counter

a = np.arange(12).reshape((3, 4))
b = a.copy()

add_lst = [0, 2, 0, 2, 0]
add_lst = Counter(add_lst)
a[1, list(add_lst.keys())] += list(add_lst.values())
print(a-b)

Outputs:

[[0 0 0 0]
 [3 0 2 0]
 [0 0 0 0]]
Related