1

I would like to enumerate the elements of a 2-dimensional NumPy array excluding the first and last row and column (i.e. the ones in the matrix below).

import numpy as np

q = np.zeros((4, 4))
q[1, 1] = 1
q[1, 2] = 1
q[2, 1] = 1
q[2, 2] = 1

# [[0. 0. 0. 0.]
#  [0. 1. 1. 0.]
#  [0. 1. 1. 0.]
#  [0. 0. 0. 0.]]

I can enumerate every element using and add conditionals checking to the first and last row/column but this seems crude:

for ij, q_ij in np.ndenumerate(q):
    print(ij, q_ij)

When I slice off the first and last columns and rows i and j are off by one

for ij, q_ij in np.ndenumerate(q[1:-1, 1:-1]):
    i, j = ij
    original_ij = (i + 1, j + 1)
    print(original_ij, ij, q_ij)

# (1, 1) (0, 0) 1.0
# (1, 2) (0, 1) 1.0
# (2, 1) (1, 0) 1.0
# (2, 2) (1, 1) 1.0

I can obviously adjust i and j to properly refer to the original matrix. For the sale of clarity, I am attempting to calculate the laplacian for a scalar field (e.g. temperature)

laplacian[i][j] = q[i+1][j] + q[i-1][j] + q[i][j+1] + q[i][j-1] - 4*a[i][j]

and need to avoid the boundary elements.

Is there a more elegant way to do this?

Frank
  • 3,029
  • 5
  • 34
  • 43
  • 1
    what is that you are trying to achieve by looping over the array. You probably better off using non-loop approaches with numpy. Looping over numpy arrays are not advised. Maybe you can elaborate on your broader problem so we can help better. Thank you – Ehsan Jan 02 '21 at 18:26
  • @Ehsan I am attempting to calculate the laplacian of the scalar field `q` (edited question to clarify). – Frank Jan 02 '21 at 20:44

2 Answers2

2

I think there's no more "elegant" form, sincerely, I will use the basic range(1,q_ij-1)* , but all is our programming manner at the end, just do it with the form that youre more confortable and works well

range(start, stop[, step])

1

Construct the array with sliced assignment:

In [164]: arr = np.zeros((4,4),int)
In [165]: arr[1:3,1:3] = np.arange(1,5).reshape(2,2)
In [166]: arr
Out[166]: 
array([[0, 0, 0, 0],
       [0, 1, 2, 0],
       [0, 3, 4, 0],
       [0, 0, 0, 0]])

Since you print each element, a double loop on the desired indices is just as good as ndenumerate (or anything else that 'hides' the indexing):

In [167]: for i in range(1,3):
     ...:     for j in range(1,3):
     ...:         print(f'({i},{j}) {arr[i,j]}')
     ...: 
(1,1) 1
(1,2) 2
(2,1) 3
(2,2) 4

There's nothing elegant about printing the elements of an array one by one with coodinates.

edit

q[i+1][j] + q[i-1][j] + q[i][j+1] + q[i][j-1] - 4*a[i][j]

can rewritten with slices as something like this (defails may not be right)

q[2:4,1:3] - q[0:2,1:3] + ... - 4*a[1:3, 1:3]

What I'm aiming for is a 2d extension of the basic difference calculation:

In [178]: x = np.array([1,0,3,2,0,1])
In [179]: x[1:] - x[:-1]
Out[179]: array([-1,  3, -1, -2,  1])

This is what np.diff(x).

In scipy.signal there's a convolve function that may be applicable. I've seen others use it, but haven't myself.

https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.convolve.html

hpaulj
  • 221,503
  • 14
  • 230
  • 353
  • I have added more context on why I want only the non-border elements (calculating the laplacian of a scalar field). Nice trick to slice in the non-border values using `np.arange` and `np.reshape` – Frank Jan 02 '21 at 20:48
  • 1
    I added a `np.diff` example. – hpaulj Jan 02 '21 at 21:57