Getting a subset of numpy array indicies an easy way

64 views Asked by At

Can this for loop be written in a simpler way?

import itertools
import numpy as np

def f(a, b, c): # placeholder for a complex function
  print(a+b+c)

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

for y, x in itertools.product(range(a.shape[0]-1), range(a.shape[1]-1)):
  f(a[y, x], a[y, x+1], a[y+1, x])

The other options I tried, look more convoluted, e.g.:

it = np.nditer(a[:-1, :-1], flags=['multi_index'])

for e in it:
  y, x = it.multi_index
  f(a[y, x], a[y, x+1], a[y+1, x])
2

There are 2 answers

1
nenad.popovic On

Posting it as an answer, and sorry if this is too obvious, but isn't this simply

for y in range(a.shape[0]-1):
     for x in range(a.shape[1]-1):
         f(a[y, x], a[y, x+1], a[y+1, x])
2
paime On

If I use your method I got:

expected = [5, 8, 11, 17, 20, 23]

but you can vectorize the computation by generating an array containing the data in a more suitable way:

a_stacked = np.stack([a[:-1, :-1], a[:-1, 1:], a[1:, :-1]], axis=0)

From there multiple solutions:

  • If you already know the function will be the sum:
>>> a_stacked.sum(axis=0)
array([[ 5,  8, 11],
       [17, 20, 23]])
  • If you know that your function is already vectorized:
>>> f(*a_stacked)
array([[ 5,  8, 11],
       [17, 20, 23]])
  • If your function does not vectorize, you can use np.vectorize for convenience (no performance improvement):
>>> np.vectorize(f)(*a_stacked)
array([[ 5,  8, 11],
       [17, 20, 23]])

Obviously you can flatten the array next.