从 N 维数组中的值构造 (N+1) 维对角矩阵

Construct (N+1)-dimensional diagonal matrix from values in N-dimensional array

我有一个N维数组。我想通过将最终维度的值放在对角线上来将其扩展为 (N+1) 维数组。

例如,使用显式循环:

In [197]: M = arange(5*3).reshape(5, 3)

In [198]: numpy.dstack([numpy.diag(M[i, :]) for i in range(M.shape[0])]).T
Out[198]: 
array([[[ 0,  0,  0],
        [ 0,  1,  0],
        [ 0,  0,  2]],

       [[ 3,  0,  0],
        [ 0,  4,  0],
        [ 0,  0,  5]],

       [[ 6,  0,  0],
        [ 0,  7,  0],
        [ 0,  0,  8]],

       [[ 9,  0,  0],
        [ 0, 10,  0],
        [ 0,  0, 11]],

       [[12,  0,  0],
        [ 0, 13,  0],
        [ 0,  0, 14]]])

这是一个5×3×3的数组。

我的实际数组很大,我想避免显式循环(隐藏 map 中的循环而不是列表理解没有性能提升;它仍然是一个循环)。尽管 numpy.diag 适用于构建常规的二维对角矩阵,但它不会扩展到更高的维度(当给定一个二维数组时,它将提取其对角线)。 numpy.diagflat 返回的数组将所有内容都变成一个大对角线,结果是一个 15×15 的数组,其中包含更多的零,无法重新整形为 5×3×3。

有没有一种方法可以从 N 维数组中的值高效地构造 (N+1) 对角矩阵,而无需多次调用 diag

将N维数组最后一维转为对角矩阵的通用方法:

我们需要降低数组的维度,对每个向量应用 numpy.diag() 函数,然后将其重建为原始维度 + 1。

将矩阵重塑为二维:

M.reshape(-1, M.shape[-1])

然后使用 map 对其应用 np.diag,并使用以下方法重建具有附加维度的矩阵:

result.reshape([*M.shape, M.shape[-1]])

所有这些结合起来得出以下结果:

result = np.array(list(map(
   np.diag,
   M.reshape(-1, M.shape[-1])
))).reshape([*M.shape, M.shape[-1]])

一个例子:

shape = np.arange(2,8)
M = np.arange(shape.prod()).reshape(shape)
print(M.shape)  # (2, 3, 4, 5, 6, 7)

result = np.array(list(map(np.diag, M.reshape(-1, M.shape[-1])))).reshape([*M.shape, M.shape[-1]])

print(result.shape)  # (2, 3, 4, 5, 6, 7, 7)

res[0,0,0,0,2,:] 包含以下内容:

array([[14,  0,  0,  0,  0,  0,  0],
       [ 0, 15,  0,  0,  0,  0,  0],
       [ 0,  0, 16,  0,  0,  0,  0],
       [ 0,  0,  0, 17,  0,  0,  0],
       [ 0,  0,  0,  0, 18,  0,  0],
       [ 0,  0,  0,  0,  0, 19,  0],
       [ 0,  0,  0,  0,  0,  0, 20]])

使用numpy.diagonal to take a view of the relevant diagonals of a properly-shaped N+1-dimensional array, force the view to be writeable with setflags,并写入视图:

expanded = numpy.zeros(M.shape + M.shape[-1:], dtype=M.dtype)

diagonals = numpy.diagonal(expanded, axis1=-2, axis2=-1)
diagonals.setflags(write=True)

diagonals[:] = M

这会生成您想要的数组 expanded

您可以使用无处不在的 np.einsum 的几乎不可能猜测的功能。当按如下方式使用时 einsum 将 return 广义对角线的可写视图:

>>> import numpy as np
>>> M = np.arange(5*3).reshape(5, 3)
>>> 
>>> out = np.zeros((*M.shape, M.shape[-1]), M.dtype)
>>> np.einsum('...jj->...j', out)[...] = M
>>> out
array([[[ 0,  0,  0],
        [ 0,  1,  0],
        [ 0,  0,  2]],

       [[ 3,  0,  0],
        [ 0,  4,  0],
        [ 0,  0,  5]],

       [[ 6,  0,  0],
        [ 0,  7,  0],
        [ 0,  0,  8]],

       [[ 9,  0,  0],
        [ 0, 10,  0],
        [ 0,  0, 11]],

       [[12,  0,  0],
        [ 0, 13,  0],
        [ 0,  0, 14]]])