【问题标题】:Get all the diagonals in a matrix/list of lists in Python在Python中获取矩阵/列表列表中的所有对角线
【发布时间】:2011-09-12 21:17:59
【问题描述】:

我正在寻找一种 Pythonic 方法来获取(方形)矩阵的所有对角线,表示为列表的列表。

假设我有以下矩阵:

matrix = [[-2,  5,  3,  2],
          [ 9, -6,  5,  1],
          [ 3,  2,  7,  3],
          [-1,  8, -4,  8]]

那么大对角线就简单了:

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]

但是我很难想出一种方法来生成所有对角线。我正在寻找的输出是:

[[-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]]

【问题讨论】:

    标签: python matrix diagonal


    【解决方案1】:

    numpy 中可能有比下面更好的方法,但我还不太熟悉:

    import numpy as np
    
    matrix = np.array(
             [[-2,  5,  3,  2],
              [ 9, -6,  5,  1],
              [ 3,  2,  7,  3],
              [-1,  8, -4,  8]])
    
    diags = [matrix[::-1,:].diagonal(i) for i in range(-3,4)]
    diags.extend(matrix.diagonal(i) for i in range(3,-4,-1))
    print [n.tolist() for n in diags]
    

    输出

    [[-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]]
    

    编辑:更新为适用于任何矩阵大小。

    import numpy as np
    
    # Alter dimensions as needed
    x,y = 3,4
    
    # create a default array of specified dimensions
    a = np.arange(x*y).reshape(x,y)
    print a
    print
    
    # a.diagonal returns the top-left-to-lower-right diagonal "i"
    # according to this diagram:
    #
    #  0  1  2  3  4 ...
    # -1  0  1  2  3
    # -2 -1  0  1  2
    # -3 -2 -1  0  1
    #  :
    #
    # You wanted lower-left-to-upper-right and upper-left-to-lower-right diagonals.
    #
    # The syntax a[slice,slice] returns a new array with elements from the sliced ranges,
    # where "slice" is Python's [start[:stop[:step]] format.
    
    # "::-1" returns the rows in reverse. ":" returns the columns as is,
    # effectively vertically mirroring the original array so the wanted diagonals are
    # lower-right-to-uppper-left.
    #
    # Then a list comprehension is used to collect all the diagonals.  The range
    # is -x+1 to y (exclusive of y), so for a matrix like the example above
    # (x,y) = (4,5) = -3 to 4.
    diags = [a[::-1,:].diagonal(i) for i in range(-a.shape[0]+1,a.shape[1])]
    
    # Now back to the original array to get the upper-left-to-lower-right diagonals,
    # starting from the right, so the range needed for shape (x,y) was y-1 to -x+1 descending.
    diags.extend(a.diagonal(i) for i in range(a.shape[1]-1,-a.shape[0],-1))
    
    # Another list comp to convert back to Python lists from numpy arrays,
    # so it prints what you requested.
    print [n.tolist() for n in diags]
    

    输出

    [[ 0  1  2  3]
     [ 4  5  6  7]
     [ 8  9 10 11]]
    
    [[0], [4, 1], [8, 5, 2], [9, 6, 3], [10, 7], [11], [3], [2, 7], [1, 6, 11], [0, 5, 10], [4, 9], [8]]
    

    【讨论】:

      【解决方案2】:

      我遇到了这个问题的另一个有趣的解决方案。 通过查看 x 和 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     
      

      从图中您可以看到,使用这些方程式可以唯一地识别每个对角线和轴。从每个表中获取每个唯一编号并为该标识符创建一个容器。

      请注意,后向对角线已偏移以从零索引开始,并且前向对角线的长度始终等于后向对角线的长度。

      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)
      

      哪个会打印

      [[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]]
      

      【讨论】:

      • 这是我见过这个问题的最佳答案。
      • 我喜欢这个答案,因为它作为代码具有灵活性,并且还演示了矩阵中的元素与其 x y 坐标之间的数学关系。
      【解决方案3】:

      从向上和向右倾斜的对角线开始。

      如果 (x,y) 是矩阵内的直角坐标,您想要转换到/从坐标方案 (p,q) 转换,其中 p 是对角线的编号,q 是沿对角线的索引。 (所以 p=0 是 [-2] 对角线,p=1 是 [9,5] 对角线,p=2 是 [3,-6,3] 对角线,依此类推。)

      要将 (p,q) 转换为 (x,y),您可以使用:

      x = q
      y = p - q
      

      尝试插入 p 和 q 的值,看看它是如何工作的。

      现在您只需循环...对于从 0 到 2N-1 的 p,以及从 max(0, p-N+1) 到 min(p, N-1) 的 q。将 p,q 转换为 x,y 并打印。

      然后对于其他对角线,重复循环但使用不同的变换:

      x = N - 1 - q
      y = p - q
      

      (这实际上只是左右翻转矩阵。)

      对不起,我实际上并没有用 Python 编写代码。 :-)

      【讨论】:

      • 查看 here 了解 Python 实现。
      【解决方案4】:

      这是给Moe的,谁问过a similar question

      我首先制作简单的函数来复制任何矩形矩阵的行或列。

      def get_rows(grid):
          return [[c for c in r] for r in grid]
      
      def get_cols(grid):
          return zip(*grid)
      

      使用这两个函数,然后我通过在每行的开始/结束处添加一个递增/递减缓冲区来获得对角线。然后我得到这个缓冲网格的列,然后删除每列上的缓冲区。即)

      1 2 3    |X|X|1|2|3|    | | |1|2|3|
      4 5 6 => |X|4|5|6|X| => | |4|5|6| | => [[7],[4,8],[1,5,9],[2,6],[3]]
      7 8 9    |7|8|9|X|X|    |7|8|9| | |
      

      .

      def get_backward_diagonals(grid):
          b = [None] * (len(grid) - 1)
          grid = [b[i:] + r + b[:i] for i, r in enumerate(get_rows(grid))]
          return [[c for c in r if c is not None] for r in get_cols(grid)]
      
      def get_forward_diagonals(grid):
          b = [None] * (len(grid) - 1)
          grid = [b[:i] + r + b[i:] for i, r in enumerate(get_rows(grid))]
          return [[c for c in r if c is not None] for r in get_cols(grid)]
      

      【讨论】:

        【解决方案5】:

        我最近重新发明了这个轮子。这是一种易于重用/扩展的方法,可以在方形列表列表中找到对角线:

        def get_diagonals(grid, bltr = True):
          dim = len(grid)
          assert dim == len(grid[0])
          return_grid = [[] for total in xrange(2 * len(grid) - 1)]
          for row in xrange(len(grid)):
            for col in xrange(len(grid[row])):
              if bltr: return_grid[row + col].append(grid[col][row])
              else:    return_grid[col - row + (dim - 1)].append(grid[row][col])
          return return_grid
        

        假设列表索引:

        00 01 02 03

        10 11 12 13

        20 21 22 23

        30 31 32 33

        然后设置bltr = True(默认),返回从左下角到右上角的对角线,即

        00           # row + col == 0
        10 01        # row + col == 1
        20 11 02     # row + col == 2
        30 21 12 03  # row + col == 3
        31 22 13     # row + col == 4
        32 23        # row + col == 5
        33           # row + col == 6
        

        设置bltr = False,返回从左下角到右上角的对角线,即

        30            # (col - row) == -3
        20 31         # (col - row) == -2
        10 21 32      # (col - row) == -1
        00 11 22 33   # (col - row) == 0
        01 12 23      # (col - row) == +1
        02 13         # (col - row) == +2
        03            # (col - row) == +3
        

        Here's a runnable version 使用 OP 的输入矩阵。

        【讨论】:

          【解决方案6】:

          我想现在有一种更简单的方法可以做到这一点。 (但只有在您已经熟悉上述答案时才使用它)。

          from collections import defaultdict
          

          有一个名为 defaultdict 的方法,它是从 collections 模块中导入的,如果您不知道要拥有的密钥,可以使用它来创建字典。

          我们在这些情况下使用它:

          • 如果您不知道键但想为特定键分配一些值。
          • 如果字典中不存在键,普通字典会引发 keyerror。但这不会(如果需要,您可以为其分配一些功能)

          导入后,可以运行以下代码进行检查。

          rows,cols = 3,3
          matrix = [[1, 2, 3],
                    [4, 5, 6],
                    [7, 8, 9]]
          
          diagonal1 = defaultdict(list) # For the top right to bottom left
          diagonal2 = defaultdict(list) # For the top left to bottom right
          for i in range(rows):
              for j in range(cols):
                  diagonal1[i-j].append(matrix[i][j])
                  diagonal2[i+j].append(matrix[i][j])
          print(diagonal1,'\n',diagonal2)
          

          list 参数将为该特定键创建一个值列表。

          输出如下:

          defaultdict(<class 'list'>, {0: [1, 5, 9], -1: [2, 6], -2: [3], 1: [4, 8], 2: [7]}) 
          defaultdict(<class 'list'>, {0: [1], 1: [2, 4], 2: [3, 5, 7], 3: [6, 8], 4: [9]})
          

          现在你可以随意使用两条对角线了。

          要了解有关 defaultdict 的更多信息,请使用此链接: Click here

          【讨论】:

            【解决方案7】:

            这仅适用于宽度和高度相等的矩阵。 但它也不依赖任何第三方。

            matrix = [[11, 2, 4],[4, 5, 6],[10, 8, -12]]
            
            # only works for diagnoals of equal width and height
            def forward_diagonal(matrix):
                if not isinstance(matrix, list):
                    raise TypeError("Must be of type list")
            
                results = []
                x = 0
                for k, row in enumerate(matrix):
                    # next diag is (x + 1, y + 1)
                    for i, elm in enumerate(row):
            
                        if i == 0 and k == 0:
                            results.append(elm)
                            break
                        if (x + 1 == i):
                            results.append(elm)
                            x = i
                            break
                return results
            
            print 'forward diagnoals', forward_diagonal(matrix)
            

            【讨论】:

              【解决方案8】:

              基于 Nemo 上述回答的代码:

              def print_diagonals(matrix):
                  n = len(matrix)
                  diagonals_1 = []  # lower-left-to-upper-right diagonals
                  diagonals_2 = []  # upper-left-to-lower-right diagonals
                  for p in range(2*n-1):
                      diagonals_1.append([matrix[p-q][q] for q in range(max(0, p - n + 1), min(p, n - 1) + 1)])
                      diagonals_2.append([matrix[n-p+q-1][q] for q in range(max(0, p - n + 1), min(p, n - 1) + 1)])
                  print("lower-left-to-upper-right diagonals: ", diagonals_1)
                  print("upper-left-to-lower-right diagonals: ", diagonals_2)
              
              
              print_diagonals([
                  [1, 2, 1, 1],
                  [1, 1, 4, 1],
                  [1, 3, 1, 6],
                  [1, 7, 2, 5],
              ])
              
              lower-left-to-upper-right diagonals:  [[1], [1, 2], [1, 1, 1], [1, 3, 4, 1], [7, 1, 1], [2, 6], [5]]
              upper-left-to-lower-right diagonals:  [[1], [1, 7], [1, 3, 2], [1, 1, 1, 5], [2, 4, 6], [1, 1], [1]]
              

              【讨论】:

                【解决方案9】:

                Python 方法

                对于纯 Python 实现,我建议在 1D 中工作。

                W, H = len(mat[0]), len(mat) 
                idx = range(W-1) + range(W-1, W*H, W)
                rng = range(1, W) + range(H, 0, -1)
                rng = map(lambda x: x if (x < min(W, H)) else min(W, H), rng)
                dia = [[i + (W-1) * m for m in xrange(r)] for i, r in zip(idx, rng)]
                

                这里dia 返回每​​个对角线的索引列表。要检索相应的值:

                arr = [e for row in mat for e in row] #Flatten the matrix
                for d in dia:
                    print [arr[e] for e in d][::-1]
                
                [-2]
                [9, 5]
                [3, -6, 3]
                [-1, 2, 5, 2]
                [8, 7, 1]
                [-4, 3]
                [8]
                

                如果要反方向返回值:

                arr2 = [e for row in zip(*mat[::-1]) for e in row] #Flatten and rotate the matrix by 90°
                for d in dia[::-1]:
                    print [arr2[e] for e in d]
                
                [2]
                [3, 1]
                [5, 5, 3]
                [-2, -6, 7, 8]
                [9, 2, -4]
                [3, 8]
                [-1]
                

                Numpy 方法

                tril = [np.flip(np.fliplr(mat).diagonal(n)) for n in xrange(mat.shape[0])][::-1]
                trir = [np.flipud(mat).diagonal(n) for n in xrange(1, mat.shape[0])]
                dia = tril + trir
                
                [array([-2]),
                 array([9, 5]),
                 array([ 3, -6,  3]),
                 array([-1,  2,  5,  2]),
                 array([8, 7, 1]),
                 array([-4,  3]),
                 array([8])]
                

                【讨论】:

                  【解决方案10】:

                  试试这个:

                  import numpy as np
                  matrix = [[-2,  5,  3,  2],
                            [ 9, -6,  5,  1],
                            [ 3,  2,  7,  3],
                            [-1,  8, -4,  8]]
                  
                  matrix = np.array(matrix)
                  matrix = np.flipud(matrix)
                  a = matrix.shape[0]
                  list_ = [np.diag(matrix, k=i).tolist() for i in range(-a+1,a)]
                  print(list_)
                  

                  输出

                  [[-2], [9, 5], [3, -6, 3], [-1, 2, 5, 2], [8, 7, 1], [-4, 3], [8]]
                  

                  【讨论】:

                    【解决方案11】:

                    尝试使用字典

                    mat = [[-2,  5,  3,  2],
                          [ 9, -6,  5,  1],
                          [ 3,  2,  7,  3],
                          [-1,  8, -4,  8]]
                    dct = dict()
                    for i in range(len(mat)-1,-len(mat[0]),-1):
                        dct[i] = []
                    for i in range(len(mat)):
                        for j in range(len(mat[0])):
                           dct[i-j].append(mat[i][j])
                    print(dct)
                    

                    输出:

                    {3: [-1], 2: [3, 8], 1: [9, 2, -4], 0: [-2, -6, 7, 8], -1: [5, 5, 3], -2: [3, 1], -3: [2]}
                    

                    【讨论】:

                      【解决方案12】:

                      使用迭代工具

                      matrix = [[-2,  5,  3,  2],
                            [ 9, -6,  5,  1],
                            [ 3,  2,  7,  3],
                            [-1,  8, -4,  8]]
                      
                      import itertools as it
                      
                      def show_diagonals(alist):
                      
                          # get row/col lenght
                          a = len(alist)
                      
                          # creating a fliped matrix
                          rlist = []
                          for r in alist:
                              new = r.copy()
                              new.reverse()
                              rlist.append(new)
                      
                          flatten_list = list(it.chain.from_iterable(alist)) 
                          flatten_rlist = list(it.chain.from_iterable(rlist)) 
                          b = len(flatten_list)
                          first_diag = list(it.islice(flatten_list, 0, b+1, a+1))
                          second_diag = list(it.islice(flatten_rlist, 0, b+1, a+1))
                          return first_diag, second_diag
                      
                      a, b = show_diagonals(matrix)
                      

                      【讨论】:

                        【解决方案13】:

                        使用一些 numpy-fu 获取主对角线:

                        import numpy as np  
                        r = np.arange(36) 
                        r.resize((6, 6)) 
                        print(r) 
                        r = r.reshape(len(r)**2)[::len(r)+1] 
                        print(r)
                        

                        打印:

                        [[ 0  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 26 27 28 29]
                         [30 31 32 33 34 35]]
                        
                        [ 0  7 14 21 28 35]
                        

                        【讨论】:

                        • 您应该添加一些解释、可视化或原始输出以使您的答案更清晰。
                        【解决方案14】:

                        从这里:np.Diagonal

                         np.diagonal(matrix)
                        

                        【讨论】:

                        • 这只会返回大对角线array([-2, -6, 7, 8]),而不是所有其他对角线。
                        猜你喜欢
                        • 2020-10-21
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2023-04-08
                        • 2014-02-08
                        • 2012-03-28
                        • 2016-02-15
                        相关资源
                        最近更新 更多