Numpy 在所有元素之间进行乘积,然后插入到三角形二维数组中

Numpy make the product between all elemens and then insert into a triangular 2d array

假设我们得到下面的一维数组

arr = np.array([a,b,c])

我需要做的第一件事是制作所有元素的乘积,即

[ab,ac,bc]

然后用这个元素构造一个二维三角数组

[
[a,ab,ac],
[0,b,bc],
[0,0,c]
]

一种方法是计算一维数组的外积,然后根据您只需要二维三角矩阵的上三角的知识使用掩码。

import numpy as np

a = np.array([5,4,3])
n = len(a)

outer = np.outer(a, a)
outer[np.tril_indices(n)] = 0
outer[np.diag_indices(n)] = a

outer
array([[ 5, 20, 15],
       [ 0,  4, 12],
       [ 0,  0,  3]])

用你的一维数组创建一条对角线,并用 outer:

的上三角填充它的上三角
out = np.diag(arr)
#upper triangle indices
uidx = np.triu_indices(arr.size,k=1)
#replacing upper triangle with outer
out[uidx]=np.outer(arr,arr)[uidx]

我们可以使用遮罩来达到我们想要的效果,像这样-

def upper_outer(a):
    out = a[:,None]*a
    out[np.tri(len(a), k=-1, dtype=bool)] = 0
    np.fill_diagonal(out,a)
    return out

样本运行-

In [84]: a = np.array([3,6,2])

In [86]: upper_outer(a)
Out[86]: 
array([[ 3, 18,  6],
       [ 0,  6, 12],
       [ 0,  0,  2]])

基准测试

其他方法:

# @Nick Becker's soln
def tril_diag(a):
    n = len(a)
    outer = np.outer(a, a)
    outer[np.tril_indices(n)] = 0
    outer[np.diag_indices(n)] = a
    return outer

# @Ehsan's soln
def triu_outer(arr):
    out = np.diag(arr)
    uidx = np.triu_indices(arr.size,k=1)
    out[uidx]=np.outer(arr,arr)[uidx]
    return out

使用 benchit 包(几个基准测试工具打包在一起;免责声明:我是它的作者)对提议的解决方案进行基准测试。

import benchit
in_ = [np.random.rand(n) for n in [10,100,200,500,1000,5000]]
funcs = [upper_outer, tril_diag, triu_outer]
t = benchit.timings(funcs, in_)
t.rank()
t.plot(logx=True, save='timings.png')

对于大型数据集,我们还可以使用numexpr来利用多核-

import numexpr as ne

def upper_outer_v2(a):
    mask = ~np.tri(len(a), dtype=bool)
    out = ne.evaluate('a2D*a*mask',{'a2D':a[:,None], 'a':a, 'mask':mask})
    np.fill_diagonal(out,a)
    return out

新时序图:

有一个 blas 函数(几乎)用于:

# example
a = np.array([1.,2.,5.])

from scipy.linalg.blas import dsyr

# apply blas function; transpose since blas uses FORTRAN order
out = dsyr(1,a,1).T
# fix diagonal
out.reshape(-1)[::a.size+1] = a
out
# array([[ 1.,  2.,  5.],
#        [ 0.,  2., 10.],
#        [ 0.,  0.,  5.]])

benchit(感谢@Divakar)