5

What is the Pythonic way to get a list of diagonal elements in a matrix passing through entry (i,j)?

For e.g., given a matrix like:

[1  2  3   4  5]
[6  7  8   9 10]
[11 12 13 14 15]
[16 17 18 19 20]
[21 22 23 24 25]

and an entry, say, (1,3) (representing element 9) how can I get the elements in the diagonals passing through 9 in a Pythonic way? Basically, [3,9,15] and [5,9,13,17,21] both.

smci
  • 32,567
  • 20
  • 113
  • 146

2 Answers2

11

Using np.diagonal with a little offset logic.

import numpy as np

lst = np.array([[1,  2,  3,   4,  5],
                [6,  7,  8,   9, 10],
                [11, 12, 13, 14, 15],
                [16, 17, 18, 19, 20],
                [21, 22, 23, 24, 25]])


i, j = 1, 3
major = np.diagonal(lst, offset=(j - i))
print(major)
array([ 3,  9, 15])

minor = np.diagonal(np.rot90(lst), offset=-lst.shape[1] + (j + i) + 1)
print(minor)
array([ 5,  9, 13, 17, 21])

The indices i and j are the row and column. By specifying the offset, numpy knows from where to begin selecting elements for the diagonal.

For the major diagonal, You want to start collecting from 3 in the first row. So you need to take the current column index and subtract it by the current row index, to figure out the correct column index at the 0th row. Similarly for the minor diagonal, where the array is flipped (rotated by 90˚) and the process repeats.

cs95
  • 379,657
  • 97
  • 704
  • 746
  • Thank you. Could you kindly explain the parameters you have passed to the diagonal method? –  Sep 09 '17 at 23:22
  • @UmedhSinghBundela Explained to the best of my ability. – cs95 Sep 09 '17 at 23:24
  • 1
    For the second case I am guessing : `np.diagonal(np.rot90(lst), offset=-lst.shape[1] + (j + i) + 1)`. Consider : `lst = np.arange(35).reshape(5,7)+1` to get the idea there. – Divakar Sep 10 '17 at 04:24
  • @Divakar Thank you so much! Yeah, it was incorrect for that particular input. – cs95 Sep 10 '17 at 04:26
  • 1
    You can write `np.diagonal(lst, offset=(j - i))` way more simply as `lst.diagonal(j-i)`. – smci Mar 25 '20 at 19:36
0

As another alternative method, with raveling the array and for matrix with shape (n*n):

array = np.array([[1,  2,  3,   4,  5],
                  [6,  7,  8,   9, 10],
                  [11, 12, 13, 14, 15],
                  [16, 17, 18, 19, 20],
                  [21, 22, 23, 24, 25]])

x, y = 1, 3


a_mod = array.ravel()
size = array.shape[0]

if y >= x:
    diag = a_mod[y-x:(x+size-y)*size:size+1]
else:
    diag = a_mod[(x-y)*size::size+1]

if x-(size-1-y) >= 0:
    reverse_diag = array[:, ::-1].ravel()[(x-(size-1-y))*size::size+1]
else:
    reverse_diag = a_mod[x:x*size+1:size-1]

# diag         --> [ 3  9 15]
# reverse_diag --> [ 5  9 13 17 21]

The correctness of the resulted arrays must be checked further. This can be developed to handle matrices with other shapes e.g. (n*m).

Ali_Sh
  • 2,667
  • 3
  • 43
  • 66