Copy flat list of upper triangle entries to full matrix?

前端 未结 4 1327
礼貌的吻别
礼貌的吻别 2021-01-21 13:53

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

相关标签:
4条回答
  • 2021-01-21 14:21

    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
    
    0 讨论(0)
  • 2021-01-21 14:23

    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.

    0 讨论(0)
  • 2021-01-21 14:27

    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)
    
    0 讨论(0)
  • 2021-01-21 14:41

    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.

    0 讨论(0)
提交回复
热议问题