I have the upper triangle entries (including diagonal) of a symmetric matrix in a flat list (concatenated rows), and I want to use them to fill in the full matrix, including the
Inspired by this solution, you can use boolean indexing to set elements and as such might be pretty efficient. Here's one way to implement it -
def mask_based_utri2mat(utri,ntotal):
# Setup output array
out = np.empty((ntotal,ntotal))
# Create upper triang. mask
mask = np.triu(np.ones((ntotal,ntotal),dtype=bool))
# Set upper triang. elements with mask
out[mask] = utri
# Set lower triang. elements with transposed mask
out.T[mask] = utri
return out
Runtime tests -
In [52]: # Inputs
...: ntotal = 100
...: utri = np.random.rand(np.triu_indices(ntotal)[0].size)
...:
In [53]: np.allclose(mask_based_utri2mat(utri,ntotal),utri2mat(utri,ntotal))
Out[53]: True
In [54]: %timeit utri2mat(utri,ntotal)
1000 loops, best of 3: 270 µs per loop
In [55]: %timeit mask_based_utri2mat(utri,ntotal)
10000 loops, best of 3: 127 µs per loop
In [56]: # Inputs
...: ntotal = 1000
...: utri = np.random.rand(np.triu_indices(ntotal)[0].size)
...:
In [57]: np.allclose(mask_based_utri2mat(utri,ntotal),utri2mat(utri,ntotal))
Out[57]: True
In [58]: %timeit utri2mat(utri,ntotal)
10 loops, best of 3: 53.9 ms per loop
In [59]: %timeit mask_based_utri2mat(utri,ntotal)
100 loops, best of 3: 15.1 ms per loop
Here's my nomination for a faster, and possibly better, way to make a symmetric matrix from flat values:
def make_sym(val, n):
# uses boolean mask
# uses the same lower tri as np.triu
mask = ~np.tri(5,k=-1,dtype=bool)
out = np.zeros((n,n),dtype=val.dtype)
out[mask] = val
out.T[mask] = val
return out
testing:
In [939]: val=np.arange(1,16)
In [940]: make_sym(val, 5)
Out[940]:
array([[ 1, 2, 3, 4, 5],
[ 2, 6, 7, 8, 9],
[ 3, 7, 10, 11, 12],
[ 4, 8, 11, 13, 14],
[ 5, 9, 12, 14, 15]])
Like the other answers it uses out.T[]
to assign the lower triangle.
Warren's answer uses np.triu_indices
, which are the where
values. This type of indexing is a bit slower than boolean masking.
But as I noted the np.triu
that Divakar uses does not return a boolean mask in earlier numpy
versions (e.g. 1.9). This is what prompted me to dig into the issue.
In 1.10 this function was rewritten as:
mask = np.tri(*m.shape[-2:], k=k-1, dtype=bool)
return np.where(mask, np.zeros(1, m.dtype), m)
I gain a bit of speed by replacing the where
with ~mask
. Same result, but just cutting out an intermediate step.
Assuming that you have a vector containing the upper triangular values of a symmetric matrix (n x n) then you can re-build the full matrix as follows:
import numpy as np
# dimension of the full matrix
n = 80
# artificial upper triangle entries n(n-1) / 2 if matrix is symmetric
entries = np.array(range((80*79) / 2))
full_matrix = np.zeros((n,n))
inds = np.triu_indices_from(full_matrix, k = 1)
full_matrix[inds] = entries
full_matrix[(inds[1], inds[0])] = entries
print(full_matrix)
Verify:
np.allclose(full_matrix, full_matrix.T)
This version is a slight variation of yours:
import numpy as np
def utri2mat(utri):
n = int(-1 + np.sqrt(1 + 8*len(utri))) // 2
iu1 = np.triu_indices(n)
ret = np.empty((n, n))
ret[iu1] = utri
ret.T[iu1] = utri
return ret
I replaced
ret = ret + ret.transpose() - np.diag(ret.diagonal())
with an assignment of utri
directly to the transpose of ret
:
ret.T[iu1] = utri
I also removed the argument ntotal
and instead computed what n
has to be based on the length of utri
.