Python 在2D numpy阵列的子矩阵上高效运行

Python 在2D numpy阵列的子矩阵上高效运行,python,arrays,numpy,matrix,Python,Arrays,Numpy,Matrix,我无法有效地执行以下矩阵运算。从一个正方形矩阵(2D numpy数组)和跨越矩阵每个索引的组(字典:键是组,值是组中矩阵索引的列表)开始,我需要获得一个新的更小的矩阵,它包含原始矩阵每个子矩阵中元素的和。子矩阵是根据群的指数定义的。因此,新矩阵也将是正方形,但其维数为组数 让我们看一下以下示例: import numpy as np X = np.arange(49).reshape((7, 7)) d = {0: [0, 1], 1: [2, 3, 4], 2: [5, 6]} def

我无法有效地执行以下矩阵运算。从一个正方形矩阵(2D numpy数组)和跨越矩阵每个索引的组(字典:键是组,值是组中矩阵索引的列表)开始,我需要获得一个新的更小的矩阵,它包含原始矩阵每个子矩阵中元素的和。子矩阵是根据群的指数定义的。因此,新矩阵也将是正方形,但其维数为组数

让我们看一下以下示例:

import numpy as np

X = np.arange(49).reshape((7, 7))

d = {0: [0, 1], 1: [2, 3, 4], 2: [5, 6]}

def get_new_matrix(matrix, groups_indexes):
    groups_number = len(groups_indexes)
    new_matrix = np.zeros((groups_number, groups_number))
    for i in range(groups_number):
        for j in range(groups_number):
            new_matrix[i][j] = np.sum(matrix[groups_indexes[i]][:,groups_indexes[j]])
    return new_matrix

Z = get_new_matrix(X, d)
print(Z)

查看结果,例如在(第二)行1和(第三)列2中,我们注意到结果是159,这是:

Z[1,2]
这意味着在原始矩阵中,由行中的组1和列中的组2(即行2、3和4以及列5和6)定义的子矩阵明确表示为:

X[[2, 3, 4]][:,[5, 6]]
np.sum(X[[2, 3, 4]][:,[5, 6]])
子矩阵中所有元素之和为19+20+26+27+33+34=159

明确地说:

X[[2, 3, 4]][:,[5, 6]]
np.sum(X[[2, 3, 4]][:,[5, 6]])
有没有办法编写一个更具python风格的代码,避免两个for循环以获得新矩阵并提高整体效率?我想这应该是一个很好的索引、广播等方式,但我还没有找到更好的解决方案


我当前的代码对于大型初始矩阵(以及潜在的大型初始组数)扩展得非常厉害,因为我不仅要对任意大型初始平方矩阵运行它,而且还要在多次迭代中运行它,所以我确实需要改进它。或者,也许没有办法使代码更好,一个解释也会非常有用:)

如果您的组索引跨越整个矩阵并且是连续的,您可以将它们存储为索引而不是字典。因为每个组都以下一个组的开头结束,所以只需要存储开始索引。您当前的
d
可以重写为

d = sorted(val[0] for val in d.values())
或者,如果您不受字典格式的限制,只需

d = np.array([0, 2, 5])
我的建议是在每个维度上应用两次,一次,基本上就像您在当前循环中所做的那样,但让numpy在内部为您管理循环:

result = np.add.reduceat(np.add.reduceat(X, d, axis=0), d, axis=1)
问题输入的结果是:

array([[ 16,  39,  36],
       [129, 216, 159],
       [156, 249, 176]])
159确实是索引[1,2]中的元素


这似乎规模相当大。使用
X=np.arange(10**6)。重塑(10**3,10**3)
d=np.arange(0,10**3,10)
在我的笔记本电脑上运行大约需要2.27毫秒。我不认为这段代码可能会成为您所做任何事情的瓶颈。

您的块总是连续的吗?是的,从某种意义上说,所有索引(示例中的0到6)都必须属于某个组,而且因为在原始矩阵中重新排列行和列顺序没有问题,我想补充一点,新的
d
可以通过例如
d=[val[0]for val in d.values()]
@AndyK获得。见多识广。这实际上使整个答案不那么复杂了。非常感谢,代码现在快多了。你太棒了。干杯@乔治。很高兴你成功了。我记得我第一次发现努比·乌夫茨有方法。@Mad物理学家我一定会探索努比·乌夫茨的方法,并以你的名义喝一杯葡萄酒,因为你的昵称是最好的。