3

I have a matrix like the following:

matrix = [
['a', 'p', 'p', 'l', 'e'],
['a', 'g', 'o', 'd', 'o'],
['n', 'n', 'e', 'r', 't'],
['g', 'a', 'T', 'A', 'C'],
['m', 'i', 'c', 's', 'r'],
['P', 'o', 'P', 'o', 'P']]

And i'm trying to find all the elements below the big diagonal. But I want them in a certain way, example, the ideal output would be:

["ocAt", "PsC","or","P"]

(I want the letters in the "rising diagonal to the right" way, here the big diagonal being "PiTro")

I tried something like:

    for f in range(len(matrix) - 1, -1, -1):
      for k in range(len(matrix)-1, 0, -1):
        my_list.append(matrix[k][k-f])

But I either get an index out of range or not the good elements. If anyone has a solution, I'll be glad to know!

MM1
  • 912
  • 1
  • 10
  • 28
  • 1
    Related: [Get all the diagonals in a matrix/list of lists in Python](https://stackoverflow.com/q/6313308/7851470) – Georgy Nov 29 '19 at 11:24

4 Answers4

1

This is one way you can achieve that result:

matrix = [['a', 'p', 'p', 'l', 'e'],
          ['a', 'g', 'o', 'd', 'o'],
          ['n', 'n', 'e', 'r', 't'],
          ['g', 'a', 'T', 'A', 'C'],
          ['m', 'i', 'c', 's', 'r'],
          ['P', 'o', 'P', 'o', 'P']]
my_list = []
for f in range(1, len(matrix[0])):
    s = []
    for k in range(len(matrix[0]) - f):
        s.append(matrix[len(matrix) - k - 1][f + k])
    my_list.append(''.join(s))
print(my_list)
# ['ocAt', 'PsC', 'or', 'P']

Or using comprehension:

my_list = [''.join(matrix[len(matrix) - i - 1][j + i] for i in range(len(matrix[0]) - j))
           for j in range(1, len(matrix[0]))]

To produce every substring in each diagonal you can do:

my_list = []
for j in range(1, len(matrix[0])):
    for i1 in range(0, len(matrix[0]) - j):
        for i2 in range(i1 + 1, len(matrix[0]) - j + 1):
            s = []
            for i in range(i1, i2):
                s.append(matrix[len(matrix) - i - 1][j + i])
            my_list.append(''.join(s))
print(my_list)
# ['o', 'oc', 'ocA', 'ocAt', 'c', 'cA', 'cAt', 'A', 'At', 't', 'P', 'Ps', 'PsC', 's', 'sC', 'C', 'o', 'or', 'r', 'P']

Or equivalently:

my_list = [''.join(matrix[len(matrix) - i - 1][j + i] for i in range(i1, i2))
           for j in range(1, len(matrix[0]))
           for i1 in range(0, len(matrix[0]) - j)
           for i2 in range(i1 + 1, len(matrix[0]) - j + 1)]

--

One solution for substrings in upper diagonals:

my_list = []
for i in range(len(matrix)):
    for j1 in range(min(i + 1, len(matrix[0]))):
        for j2 in range(j1, min(i + 1, len(matrix[0]))):
            s = []
            for j in range(j1, j2 + 1):
                s.append(matrix[i - j][j])
            my_list.append(''.join(s))
print(my_list)
# ['a', 'a', 'ap', 'p', 'n', 'ng', 'ngp', 'g', 'gp', 'p', 'g',
#  'gn', 'gno', 'gnol', 'n', 'no', 'nol', 'o', 'ol', 'l', 'm',
#  'ma', 'mae', 'maed', 'maede', 'a', 'ae', 'aed', 'aede', 'e',
#  'ed', 'ede', 'd', 'de', 'e', 'P', 'Pi', 'PiT', 'PiTr', 'PiTro',
#  'i', 'iT', 'iTr', 'iTro', 'T', 'Tr', 'Tro', 'r', 'ro', 'o']
jdehesa
  • 58,456
  • 7
  • 77
  • 121
1

Other way to do it:

width = len(matrix[0])
height =len(matrix)
result = [''] * (width-1)

number_elements_to_take = width-1
for row in reversed(matrix):
    i = 0
    for element in row[width - number_elements_to_take:]:
        result[i] += element
        i+=1
    number_elements_to_take -= 1
vlemaistre
  • 3,301
  • 13
  • 30
0

Using numpy:

matrix = numpy.array([['a', 'p', 'p', 'l', 'e'],
                      ['a', 'g', 'o', 'd', 'o'],
                      ['n', 'n', 'e', 'r', 't'],
                      ['g', 'a', 'T', 'A', 'C'],
                      ['m', 'i', 'c', 's', 'r'],
                      ['P', 'o', 'P', 'o', 'P']])

result_list = [''.join(numpy.flipud(matrix[i:,i:]).diagonal())
               for i in range(1, len(matrix)-1)]
  • 2
    `numpy.flipud(matrix).diagonal(i)` should be better than `numpy.flipud(matrix[i:,i:]).diagonal()`, also you do not need to convert it to `np.array` – Sayandip Dutta Nov 29 '19 at 11:32
  • @Sayandip Dutta: Actually that would slow it down a bit, as that would flip the whole matrix in every iteration. Flipping it once, before the list comprehension, would be bettter. – Seb Nov 29 '19 at 11:50
  • I did a little testing, it is on the contrary. I think slicing in both dimensions in every loop is expensive. Performance difference in negligible though. For 1000 iterations: with slicing: `0.01560` without slicing: `0.01482`. – Sayandip Dutta Nov 29 '19 at 11:53
  • On my machine it's slower, 26us (`np.flipud(matrix).diagonal(i)`) vs 11.5us (`np.flipud(matrix[i:,i:]).diagonal()`). (Slicing is less expensive than allocating new arrays.) `umatrix.diagonal(i)` brings it down to 6.16us if `umatrix` is flipped beforehand. – Seb Nov 29 '19 at 11:59
0

Another NumPy solution using trace:

>>> list(map(np.flipud(np.array(matrix, object)).trace, range(1, len(matrix)-1)))
['ocAt', 'PsC', 'or', 'P']
Seb
  • 4,422
  • 14
  • 23