["python","matrix","diagonal"]
Get all the diagonals in a matrix/list of lists in Python

I'm looking for a Pythonic way to get all the diagonals of a (square) matrix, represented as a list of lists. Suppose I have the following matrix: matrix = [[-2, 5, 3, 2], [ 9, -6, 5, 1], [ 3, 2, 7, 3], [-1, 8, -4, 8]] Then the large diagonals are easy: l = len(matrix[0]) print([matrix[i][i] for i in range(l)]) # [-2, -6, 7, 8] print([matrix[l-1-i][i] for i in range(l-1,-1,-1)]) # [ 2, 5, 2, -1] But I have trouble coming up with a way to generate all the diagonals. The output I'm looking for is: [[-2], [9, 5], [3,-6, 3], [-1, 2, 5, 2], [8, 7, 1], [-4, 3], [8], [2], [3,1], [5, 5, 3], [-2, -6, 7, 8], [9, 2, -4], [3, 8], [-1]]
I came across another interesting solution to this issue. The row, column, forward, and backward diagonal can all be immediately discovered by looking at a combination of x and y. Column = x Row = y F-Diag = x+y B-Diag = x-y B-Diag` = x-y-MIN | 0 1 2 | 0 1 2 | 0 1 2 | 0 1 2 | 0 1 2 --|--------- --|--------- --|--------- --|--------- --|--------- 0 | 0 1 2 0 | 0 0 0 0 | 0 1 2 0 | 0 1 2 0 | 2 3 4 1 | 0 1 2 1 | 1 1 1 1 | 1 2 3 1 |-1 0 1 1 | 1 2 3 2 | 0 1 2 2 | 2 2 2 2 | 2 3 4 2 |-2 -1 0 2 | 0 1 2 From the diagram you can see that each diagonal and axis is uniquely identifiable using these equations. Take each unique number from each table and create a container for that identifier. Note that the backward diagonals have been offset to start at a zero index, and that the length of forward diagonals is always equal to the length of backward diagonals. test = [[1,2,3],[4,5,6],[7,8,9],[10,11,12]] max_col = len(test[0]) max_row = len(test) cols = [[] for _ in range(max_col)] rows = [[] for _ in range(max_row)] fdiag = [[] for _ in range(max_row + max_col - 1)] bdiag = [[] for _ in range(len(fdiag))] min_bdiag = -max_row + 1 for x in range(max_col): for y in range(max_row): cols[x].append(test[y][x]) rows[y].append(test[y][x]) fdiag[x+y].append(test[y][x]) bdiag[x-y-min_bdiag].append(test[y][x]) print(cols) print(rows) print(fdiag) print(bdiag) Which will print [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]] [[1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12]] [[1], [2, 4], [3, 5, 7], [6, 8, 10], [9, 11], [12]] [[10], [7, 11], [4, 8, 12], [1, 5, 9], [2, 6], [3]] Using a defaultdict and a lambda, this can be generalized further: from collections import defaultdict def groups(data, func): grouping = defaultdict(list) for y in range(len(data)): for x in range(len(data[y])): grouping[func(x, y)].append(data[y][x]) return list(map(grouping.get, sorted(grouping))) test = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]] cols = groups(test, lambda x, y: x) rows = groups(test, lambda x, y: y) fdiag = groups(test, lambda x, y: x + y) bdiag = groups(test, lambda x, y: x - y)