如何根据列的其余部分设置矩阵/数组对角线的效率(避免循环?)

时间:2017-02-18 22:41:02

标签: python numpy matrix indexing

我正在尝试编写一个带有函数的解决方案,并设置中心对角线值(即沿A [0,0],A [1,1],....,A [N,N] )基于对角单元下方列中的值求和。

一个例子:

A = np.array([[0, 0, 0],
              [3, 0, 0],
              [4, 2, 0]])

B = function_to_be_built(A)

B = np.array([[7, 0, 0],
              [3, 2, 0],
              [4, 2, 0]])

您可以看到单元格[0,0] = 3 + 4 = 7和单元格[1,1]的加法运算符,2 = 2。

当然,使用for循环可以相对容易地实现这一点,但代码设计在中​​央内核中,因此效率至关重要。我觉得应该有一种方法可以使用numpy来有效地实现这一点....比如使用np.tril,所以只选择对角线以下的值?

有人可以帮助我找到解决方案吗?

感谢。

3 个答案:

答案 0 :(得分:2)

你可以取A的下三角部分,按列加总,转换为对角矩阵,然后加回A

A + np.eye(A.shape[0]) * np.tril(A).sum(axis=0)

答案 1 :(得分:2)

以下是使用np.add.at

的解决方案
y, x = np.tril_indices(len(A), -1)
np.add.at(A, [x,x], A[y,x])

这会将低于对角线的列原点添加到相应的对角线元素中。如果不保证这些为零,请执行

A[np.arange(len(A)), np.arange(len(A))] = 0

之前。

请注意,在任何一种情况下,此方法都不要求上三角形为零。

如果在相同大小的矩阵上重复此操作,则可以预先计算索引并将其展平以提高性能。

# compute once
flatinds = np.ravel_multi_index((y, x), A.shape)
flatdiag = np.ravel_multi_index((x, x), A.shape)
flshdiag = (len(A)+1) * np.arange(len(A))
# use every iteration
Afl = A.ravel()
Afl[flshdiag] = 0 # only if necessary
np.add.at(Afl, flatdiag, Afl[flatinds])

无法抗拒一个非常快的人。它利用线性索引和np.add.reduceat。由于大多数总和沿着较长的列延伸,因此在计算的开始和结束时进行完整(非延迟)转置是值得的。对于N> = 5,它始终胜过所有其他竞争者。如果你可以保持矩阵(Fortran)顺序中涉及的矩阵,那么甚至可以保存两个转置,在这种情况下,它比从N == 3开始的所有其他转置要快。

# precompute this (A.shape == (N, N))
finds = np.c_[N * np.arange(N), 1 + (N+1) * np.arange(N)].ravel()[1:-2].copy()

def PPt(A):
    A = A.T.copy()
    Af = A.ravel()
    Af[:-1:N+1] = np.add.reduceat(Af[:-N], finds)[::2]
    Af[-1] = 0
    return A.T.copy()

答案 2 :(得分:1)

如果你确定对角线和上对角线都用零填充,你可以这样做:

In [43]: B = A + np.diag(A.sum(axis=0))

In [44]: B
Out[44]: 
array([[7, 0, 0],
       [3, 2, 0],
       [4, 2, 0]])

如果您不能保证这些区域为零,您可以这样做:

In [62]: B = A + np.diag(np.tril(A, -1).sum(axis=0))

In [63]: B
Out[63]: 
array([[7, 0, 0],
       [3, 2, 0],
       [4, 2, 0]])