在 Python 中获取矩阵/列表列表中的所有对角线

2025-03-05 09:17:00
admin
原创
66
摘要:问题描述:我正在寻找一种 Pythonic 方法来获取(方形)矩阵的所有对角线,以列表列表的形式表示。假设我有以下矩阵:matrix = [[-2, 5, 3, 2], [ 9, -6, 5, 1], [ 3, 2, 7, 3], [...

问题描述:

我正在寻找一种 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]]

解决方案 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]]

使用 defaultdict 和 lambda,可以进一步概括:

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)

解决方案 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 的值来查看其如何工作。

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

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

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

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

抱歉,我实际上没有用 Python 编写这段代码。:-)

解决方案 4:

我最近又重新发明了这个轮子。下面是一个易于重用/扩展的方法来查找正方形列表中的对角线:

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

这是使用 OP 的输入矩阵的可运行版本。

解决方案 5:

Itertools 来救援!带有一些不错的单行代码...

对于dimxdim方阵,

itertools.groupby(
   sorted(itertools.product(range(dim), repeat=2), key=sum),
   key=sum,
)

对于任意nrowx 的ncol情况,

itertools.groupby(
   sorted(itertools.product(range(nrow), range(ncol)), key=sum),
   key=sum,
)

这是如何工作的?我们想要查看行和列索引的所有组合 --- 这是一个乘积。按(row, col)坐标和排序以获得升序对角线(即,(0, 0)具有0和并将首先排序)。的稳定性sort和的迭代顺序product使我们沿着相同和的对角线进行顺序排序。最后,groupby将我们的对角线聚集在一起。

示例

dim = 3

>>> import itertools as it
>>> for diagonal, coords in it.groupby(
...     sorted(it.product(range(3), repeat=2), key=sum),
...     key=sum,
...):
...     print(f"{diagonal=}")
...     print("coords=", [*coords], "
")
... 
diagonal=0
coords= [(0, 0)] 

diagonal=1
coords= [(0, 1), (1, 0)] 

diagonal=2
coords= [(0, 2), (1, 1), (2, 0)] 

diagonal=3
coords= [(1, 2), (2, 1)] 

diagonal=4
coords= [(2, 2)] 

和,nrow=3ncol=2

>>> for diagonal, coords in it.groupby(
...     sorted(it.product(range(3), range(2)), key=sum), 
...     key=sum,
...):
...     print(f"{diagonal=}")
...     print("coords=", [*coords], "
")
... 
diagonal=0
coords= [(0, 0)] 

diagonal=1
coords= [(0, 1), (1, 0)] 

diagonal=2
coords= [(1, 1), (2, 0)] 

diagonal=3
coords= [(2, 1)] 

解决方案 6:

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

解决方案 7:

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

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,'
',diagonal2)

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

输出如下:

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 的更多信息,请使用此链接:
单击此处

解决方案 8:

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

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)

解决方案 9:

根据上述 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]]

解决方案 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:

尝试使用 dict

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:

使用 itertools

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)
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3970  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2740  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   79  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   87  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   74  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用