对 Python 中矩阵的选定元素求和
Summing selected elements of a matrix in Python
我有一个包含属于不同组的值的 [n x n] 矩阵,以及一个定义每个元素属于哪个组的 [1 x n] 向量。
(n 通常是 ~1E4,在这个例子中 n=4)
我想计算一个矩阵,该矩阵是将属于同一组的所有元素加在一起得到的。
我使用np.where() 来计算每组元素所在的索引。
当我使用计算出的索引时,我没有获得预期的元素,因为我 select 对位置而不是范围(我习惯了 Matlab,在那里我可以简单地 select M(idx1,idx2 )).
import numpy as np
n=4
M = np.random.rand(n,n)
print(M)
# This vector defines to which group each element belong
belongToGroup = np.array([0, 1, 0, 2])
nGroups=np.max(belongToGroup);
# Calculate a matrix obtained by summing elements belonging to the same group
M_sum = np.zeros((nGroups+1,nGroups+1))
for g1 in range(nGroups+1):
idxG1 = np.where(belongToGroup==g1)
for g2 in range(nGroups+1):
idxG2 = np.where(belongToGroup==g2)
print('g1 = ' + str(g1))
print('g2 = ' + str(g2))
print(idxG1[0])
print(idxG2[0])
print(M[idxG1[0],idxG2[0]])
print(np.sum(M[idxG1[0],idxG2[0]]))
M_sum[g1,g2]=np.sum(M[idxG1[0],idxG2[0]])
print('')
print('Example of the problem:')
print('Elements I would like to sum to obtain M_sum[0,0]')
print(M[0:2,0:2])
print('Elements that are summed instead')
print(M[[0,1],[0,1]])
问题示例:
在上面的示例中,元素 M_sum[0,0] 应该是 M[0,0]、M[0,1]、M[1,0] 和 M[1,1] 的总和
相反,它被计算为 M[0,0] 和 M[1,1]
的总和
在 MATLAB 中,使用 2 个列表(实际上是矩阵)进行索引会选择一个块。另一方面,numpy
尝试相互广播索引数组,并 returns 选择点。它的行为接近 sub2ind
在 MATLAB 中的行为。
In [971]: arr = np.arange(16).reshape(4,4)
In [972]: arr
Out[972]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
In [973]: i1, i2 = np.array([0,2,3]), np.array([1,2,0])
使用相同大小的 2 个一维数组进行索引:
In [974]: arr[i1,i2]
Out[974]: array([ 1, 10, 12])
这实际上 returns [arr[0,1], arr[2,2], arr[3,0]]
,每个匹配索引点一个元素。
但是如果我把一个索引变成 'column vector',它从行中选择,而 i2
从列中选择。
In [975]: arr[i1[:,None], i2]
Out[975]:
array([[ 1, 2, 0],
[ 9, 10, 8],
[13, 14, 12]])
MATLAB 使块索引变得容易,而个人访问则更难。在 numpy
中,块访问有点困难,尽管底层机制是相同的。
以你的例子 i1[0]
和 i2[0]
可以是这样的数组:
array([0, 2]), array([3])
(2,) (1,)
形状 (1,) 数组可以与 (2,) 或 (2,1) 数组一起广播。如果 is[0]
是 np.array([0,1,2])
,一个不能与 (2,) 数组配对的 (3,) 数组,您的代码将失败。但是对于 (2,1) 它会产生一个 (2,3) 块。
您可以使用 np.ix_
获得类似 matlab 的行为:
A = np.arange(9).reshape(3, 3)
A[[1,2],[0,2]]
# array([3, 8])
A[np.ix_([1,2],[0,2])]
# array([[3, 5],
# [6, 8]])
在幕后,np.ix_
执行@hpaulj 详细描述的操作:
np.ix_([1,2],[0,2])
# (array([[1],
# [2]]), array([[0, 2]]))
您可以按如下方式将其应用于您的具体问题:
M = np.random.randint(0, 10, (n, n))
M
# array([[6, 2, 7, 1],
# [6, 7, 9, 5],
# [9, 4, 3, 2],
# [3, 1, 7, 9]])
idx = np.array([0, 1, 0, 2])
ng = idx.max() + 1
out = np.zeros((ng, ng), M.dtype)
np.add.at(out, np.ix_(idx, idx), M)
out
# array([[25, 6, 3],
# [15, 7, 5],
# [10, 1, 9]])
顺便说一句:有一个更快但不太明显的解决方案依赖于平面索引:
np.bincount(np.ravel_multi_index(np.ix_(idx, idx), (ng, ng)).ravel(), M.ravel(), ng*ng).reshape(ng, ng)
# array([[25., 6., 3.],
# [15., 7., 5.],
# [10., 1., 9.]])
我有一个包含属于不同组的值的 [n x n] 矩阵,以及一个定义每个元素属于哪个组的 [1 x n] 向量。 (n 通常是 ~1E4,在这个例子中 n=4)
我想计算一个矩阵,该矩阵是将属于同一组的所有元素加在一起得到的。
我使用np.where() 来计算每组元素所在的索引。 当我使用计算出的索引时,我没有获得预期的元素,因为我 select 对位置而不是范围(我习惯了 Matlab,在那里我可以简单地 select M(idx1,idx2 )).
import numpy as np
n=4
M = np.random.rand(n,n)
print(M)
# This vector defines to which group each element belong
belongToGroup = np.array([0, 1, 0, 2])
nGroups=np.max(belongToGroup);
# Calculate a matrix obtained by summing elements belonging to the same group
M_sum = np.zeros((nGroups+1,nGroups+1))
for g1 in range(nGroups+1):
idxG1 = np.where(belongToGroup==g1)
for g2 in range(nGroups+1):
idxG2 = np.where(belongToGroup==g2)
print('g1 = ' + str(g1))
print('g2 = ' + str(g2))
print(idxG1[0])
print(idxG2[0])
print(M[idxG1[0],idxG2[0]])
print(np.sum(M[idxG1[0],idxG2[0]]))
M_sum[g1,g2]=np.sum(M[idxG1[0],idxG2[0]])
print('')
print('Example of the problem:')
print('Elements I would like to sum to obtain M_sum[0,0]')
print(M[0:2,0:2])
print('Elements that are summed instead')
print(M[[0,1],[0,1]])
问题示例: 在上面的示例中,元素 M_sum[0,0] 应该是 M[0,0]、M[0,1]、M[1,0] 和 M[1,1] 的总和 相反,它被计算为 M[0,0] 和 M[1,1]
的总和在 MATLAB 中,使用 2 个列表(实际上是矩阵)进行索引会选择一个块。另一方面,numpy
尝试相互广播索引数组,并 returns 选择点。它的行为接近 sub2ind
在 MATLAB 中的行为。
In [971]: arr = np.arange(16).reshape(4,4)
In [972]: arr
Out[972]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
In [973]: i1, i2 = np.array([0,2,3]), np.array([1,2,0])
使用相同大小的 2 个一维数组进行索引:
In [974]: arr[i1,i2]
Out[974]: array([ 1, 10, 12])
这实际上 returns [arr[0,1], arr[2,2], arr[3,0]]
,每个匹配索引点一个元素。
但是如果我把一个索引变成 'column vector',它从行中选择,而 i2
从列中选择。
In [975]: arr[i1[:,None], i2]
Out[975]:
array([[ 1, 2, 0],
[ 9, 10, 8],
[13, 14, 12]])
MATLAB 使块索引变得容易,而个人访问则更难。在 numpy
中,块访问有点困难,尽管底层机制是相同的。
以你的例子 i1[0]
和 i2[0]
可以是这样的数组:
array([0, 2]), array([3])
(2,) (1,)
形状 (1,) 数组可以与 (2,) 或 (2,1) 数组一起广播。如果 is[0]
是 np.array([0,1,2])
,一个不能与 (2,) 数组配对的 (3,) 数组,您的代码将失败。但是对于 (2,1) 它会产生一个 (2,3) 块。
您可以使用 np.ix_
获得类似 matlab 的行为:
A = np.arange(9).reshape(3, 3)
A[[1,2],[0,2]]
# array([3, 8])
A[np.ix_([1,2],[0,2])]
# array([[3, 5],
# [6, 8]])
在幕后,np.ix_
执行@hpaulj 详细描述的操作:
np.ix_([1,2],[0,2])
# (array([[1],
# [2]]), array([[0, 2]]))
您可以按如下方式将其应用于您的具体问题:
M = np.random.randint(0, 10, (n, n))
M
# array([[6, 2, 7, 1],
# [6, 7, 9, 5],
# [9, 4, 3, 2],
# [3, 1, 7, 9]])
idx = np.array([0, 1, 0, 2])
ng = idx.max() + 1
out = np.zeros((ng, ng), M.dtype)
np.add.at(out, np.ix_(idx, idx), M)
out
# array([[25, 6, 3],
# [15, 7, 5],
# [10, 1, 9]])
顺便说一句:有一个更快但不太明显的解决方案依赖于平面索引:
np.bincount(np.ravel_multi_index(np.ix_(idx, idx), (ng, ng)).ravel(), M.ravel(), ng*ng).reshape(ng, ng)
# array([[25., 6., 3.],
# [15., 7., 5.],
# [10., 1., 9.]])