NumPy:沿矩阵的对角线构造正方形/扩展对角矩阵
Posted
技术标签:
【中文标题】NumPy:沿矩阵的对角线构造正方形/扩展对角矩阵【英文标题】:NumPy: construct squares along diagonal of matrix / expand diagonal matrix 【发布时间】:2021-12-12 01:48:29 【问题描述】:假设你有两个数组:
index = [1, 2, 3]
counts = [2, 3, 2]
或单数数组
arr = [1, 1, 2, 2, 2, 3, 3]
如何有效地构造矩阵
[
[1, 1, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0],
[0, 0, 2, 2, 2, 0, 0],
[0, 0, 2, 2, 2, 0, 0],
[0, 0, 2, 2, 2, 0, 0],
[0, 0, 0, 0, 0, 3, 3],
[0, 0, 0, 0, 0, 3, 3]
]
使用 NumPy?
我知道
square = np.zeros((7, 7))
np.fill_diagnol(square, arr) # see arr above
生产
[
[1, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0],
[0, 0, 2, 0, 0, 0, 0],
[0, 0, 0, 2, 0, 0, 0],
[0, 0, 0, 0, 2, 0, 0],
[0, 0, 0, 0, 0, 3, 0],
[0, 0, 0, 0, 0, 0, 3]
]
我如何通过n
“扩展”对角线,其中n
是counts[index-1]
对于index[I]
指定的值
tmp = np.array((arr * N)).reshape((len(arr), len(arr))
np.floor( (tmp + tmp.T) / 2 ) # <-- this is closer
array([[1., 1., 1., 1., 1., 2., 2.],
[1., 1., 1., 1., 1., 2., 2.],
[1., 1., 2., 2., 2., 2., 2.],
[1., 1., 2., 2., 2., 2., 2.],
[1., 1., 2., 2., 2., 2., 2.],
[2., 2., 2., 2., 2., 3., 3.],
[2., 2., 2., 2., 2., 3., 3.]])
这得到了我想要的,但可能不能很好地扩展?
riffled = list(zip(index, counts))
riffled
# [(1, 2), (2, 3), (3, 2)]
a = np.zeros((len(arr), len(arr))) # 7, 7 square
last = 0 # <-- keep track of current sub square
for i, c in riffled:
a[last:last+c, last:last+c] = np.ones((c, c)) * i
last += c # <-- shift square
产量
array([[1., 1., 0., 0., 0., 0., 0.],
[1., 1., 0., 0., 0., 0., 0.],
[0., 0., 2., 2., 2., 0., 0.],
[0., 0., 2., 2., 2., 0., 0.],
[0., 0., 2., 2., 2., 0., 0.],
[0., 0., 0., 0., 0., 3., 3.],
[0., 0., 0., 0., 0., 3., 3.]])
【问题讨论】:
np.equal.outer(arr, arr) * arr
@user3483203 也可以!谢谢
【参考方案1】:
这是一个通用的解决方案。
从索引/计数开始:
index = [1, 2, 1]
counts = [2, 3, 2]
arr = np.repeat(index, counts)
arr2 = np.repeat(range(len(index)), counts)
np.where(arr2 == arr2[:, None], arr, 0)
输出:
array([[1, 1, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0],
[0, 0, 2, 2, 2, 0, 0],
[0, 0, 2, 2, 2, 0, 0],
[0, 0, 2, 2, 2, 0, 0],
[0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 0, 1, 1]])
从数组版本开始:
arr = np.array([1, 1, 2, 2, 2, 1, 2])
arr2 = np.cumsum(np.diff(arr,prepend=np.nan) != 0)
np.where(arr2 == arr2[:, None], arr, 0)
输出:
array([[1, 1, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0],
[0, 0, 2, 2, 2, 0, 0],
[0, 0, 2, 2, 2, 0, 0],
[0, 0, 2, 2, 2, 0, 0],
[0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 2]])
【讨论】:
【参考方案2】:尝试广播:
idx = np.repeat(np.arange(len(counts)), counts)
np.where(idx==idx[:,None], arr, 0)
# or
# arr * (idx==idx[:,None])
输出;
array([[1, 1, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0],
[0, 0, 2, 2, 2, 0, 0],
[0, 0, 2, 2, 2, 0, 0],
[0, 0, 2, 2, 2, 0, 0],
[0, 0, 0, 0, 0, 3, 3],
[0, 0, 0, 0, 0, 3, 3]])
【讨论】:
行得通!读者注意arr
有问题是一个 Python 列表,因此要使其正常工作,请通过np.array(arr)
将其转换为 numpy
@SumNeuron。你可以只做np.reshape(arr, (-1, 1)) == arr
来避免显式转换。不过,进行显式转换会更便宜。
请注意,这仅适用于arr
中的块不同(例如,arr = np.array([1, 1, 2, 2, 2, 1, 1])
不起作用)
@mozway 只是出于好奇,您是否对该用例进行了调整?
@mozway 查看更新的答案。我们根本不使用npindex
作为参考。【参考方案3】:
您可以使用 scipy.linalg.block_diag 来完成这项工作:
import numpy as np
import scipy.linalg as linalg
a = 1*np.ones((2,2))
b = 2*np.ones((3,3))
c = 3*np.ones((2,2))
superBlock = linalg.block_diag(a,b,c)
print(superBlock)
#returns
#[[1. 1. 0. 0. 0. 0. 0.]
# [1. 1. 0. 0. 0. 0. 0.]
# [0. 0. 2. 2. 2. 0. 0.]
# [0. 0. 2. 2. 2. 0. 0.]
# [0. 0. 2. 2. 2. 0. 0.]
# [0. 0. 0. 0. 0. 3. 3.]
# [0. 0. 0. 0. 0. 3. 3.]]
如果您想从值列表和计数列表中到达那里,您可以这样做:
values = [1,2,3]
counts = [2,3,2]
mats = []
for v,c in zip(values,counts):
thisMatrix = v*np.ones((c,c))
mats.append( thisMatrix )
superBlock = linalg.block_diag(*mats)
print(superBlock)
【讨论】:
那么mats = [np.full((c, c), v) for v, c in zip(values, counts)]
?
这是个好办法。
如果用户只需要在这个创建步骤之后使用密集矩阵,这是完美的。但是,如果所有数据都可以像示例一样稀疏地描述,那么使用稀疏版本也可能有一些好处:scipy.sparse.block_diag docs.scipy.org/doc/scipy/reference/generated/…
@MikeHolcomb 想要提出修改建议?有一个更好的答案可能会很棒(或者自己做我会赞成的)以上是关于NumPy:沿矩阵的对角线构造正方形/扩展对角矩阵的主要内容,如果未能解决你的问题,请参考以下文章