从 NumPy 或 PyTorch 中的矩阵获取对角线 "stripe"

Getting diagonal "stripe" from matrix in NumPy or PyTorch

我需要得到矩阵的对角“条纹”。假设我有一个大小为 KxN (K>N) 的矩阵:

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

我需要从中提取对角条纹,在本例中,是通过截断原始条纹创建的矩阵 MxV 大小:

[[ 0  x  x]
 [ 3  4  x]
 [ x  7  8]
 [ x  x  11]]

所以结果矩阵为:

[[ 0  4  8]
 [ 3  7  11]]

我可以像这样定义一个布尔掩码:

import numpy as np

X=np.arange(12).reshape(4,3)
mask=np.asarray([
  [ True,  False,  False],
  [ True,  True,  False], 
  [ False, True,  True], 
  [ False, False,  True]
])

>>> X
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])

>>> X.T[mask.T].reshape(3,2).T
array([[ 0,  4,  8],
       [ 3,  7, 11]])

但我看不出如何将这样的掩码自动生成到任意 KxN 矩阵(例如 39x9、360x96)

numpyscipypytorch 中是否有自动执行此操作的函数?


附加问题:是否可以改用“反条纹”?即

[[ x   x   2]
 [ x   4   5]
 [ 6   7   x]
 [ 9   x   x]]

stride_tricks 试试看:

>>> import numpy as np
>>> 
>>> def stripe(a):
...    a = np.asanyarray(a)
...    *sh, i, j = a.shape
...    assert i >= j
...    *st, k, m = a.strides
...    return np.lib.stride_tricks.as_strided(a, (*sh, i-j+1, j), (*st, k, k+m))
... 
>>> a = np.arange(24).reshape(6, 4)
>>> a
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23]])
>>> stripe(a)
array([[ 0,  5, 10, 15],
       [ 4,  9, 14, 19],
       [ 8, 13, 18, 23]])

如果 a 是一个数组,这将创建一个可写视图,这意味着如果您愿意,您可以做类似

的事情
>>> stripe(a)[...] *= 10
>>> a
array([[  0,   1,   2,   3],
       [ 40,  50,   6,   7],
       [ 80,  90, 100,  11],
       [ 12, 130, 140, 150],
       [ 16,  17, 180, 190],
       [ 20,  21,  22, 230]])

更新:左下角到右上角的条纹可以​​以同样的精神获得。唯一的小麻烦:它不是基于与原始数组相同的地址。

>>> def reverse_stripe(a):
...     a = np.asanyarray(a)
...     *sh, i, j = a.shape
...     assert i >= j
...     *st, k, m = a.strides
...     return np.lib.stride_tricks.as_strided(a[..., j-1:, :], (*sh, i-j+1, j), (*st, k, m-k))
... 
>>> a = np.arange(24).reshape(6, 4)
>>> reverse_stripe(a)
array([[12,  9,  6,  3],
       [16, 13, 10,  7],
       [20, 17, 14, 11]])

扩展 Paul 的回答。您可以在 PyTorch 中多次使用 diag 执行相同的操作(我认为在 PyTorch 中没有任何直接的功能可以大步前进)

import torch

def stripe(a):
    i, j = a.size()
    assert(i>=j)
    out = torch.zeros((i-j+1, j))
    for diag in range(0, i-j+1):
        out[diag] = torch.diag(a, -diag)
    return out
 

a = torch.randn((6, 3))
>>> a
 0.7669  0.6808 -0.6102
-1.0624 -1.2016 -0.7308
 1.4054 -1.0621  0.2618
-0.9505 -0.9322 -0.4321
-0.0134 -1.3684  0.1883
-0.8499  0.2533 -0.3976
[torch.FloatTensor of size 6x3]
>>> stripe(a)
 0.7669 -1.2016  0.2618
-1.0624 -1.0621 -0.4321
 1.4054 -0.9322  0.1883
-0.9505 -1.3684 -0.3976
[torch.FloatTensor of size 4x3]

是的,您可以在 NumPy 中通过为其内置的 numpy.diagonal():

提供一个 offset 值来做到这一点
a = np.array([[0, 1, 2],
              [3, 4, 5],
              [6, 7, 8],
              [9,10,11]])

stripe = np.array([a.diagonal(), 
                   a.diagonal(-1)])
>>> stripe
array([[ 0,  4,  8],
       [ 3,  7, 11]])

对于任意 KxN 矩阵,您可以获得可变宽度的条纹:

stripe = [a.diagonal(i) for i in range(K,N,-1)]

PyTorchtorch.diagonal() 具有完全相同的功能。