Numpy сделать произведение между всеми элементами, а затем вставить в массив tri angular 2d - PullRequest
5 голосов
/ 10 июля 2020

Предположим, у нас есть одномерный массив ниже

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

Первое, что мне нужно сделать, это сделать произведение всех элементов, т.е.

[ab,ac,bc]

Затем построить 2d tri angular массив с этим элементом

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

Ответы [ 4 ]

5 голосов
/ 10 июля 2020

Создайте диагональ из вашего одномерного массива и заполните его верхний треугольник верхним треугольником 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]
3 голосов
/ 10 июля 2020

Существует функция 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)

введите описание изображения здесь

3 голосов
/ 10 июля 2020

Мы можем использовать маскирование для достижения желаемого результата, например:

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')

enter image description here

For large datasets, we can also use numexpr to leverage multi-cores -

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

New timings plot :

введите описание изображения здесь

3 голосов
/ 10 июля 2020

Один из способов сделать это - вычислить внешнее произведение вашего 1-мерного массива, а затем использовать маскирование, основанное на знании того, что вам нужен только верхний треугольник 2-мерной angular матрицы.

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]])
...