I would like to create a matrix using a list whose elements would be the elements of the matrix under the diagonal.
import numpy as np
x1 = np.array([0.9375, 0.75, 0.4375, 0.0, 0.9375, 0.75, 0.4375, 0.9375, 0.75, 0.9375])
x1
the matrix I would like to have is
array([[ 1. , 0.9375, 0.75 , 0.4375, 0. ],
[ 0.9375, 1. , 0.9375, 0.75 , 0.4375],
[ 0.75 , 0.9375, 1. , 0.9375, 0.75 ],
[ 0.4375, 0.75 , 0.9375, 1. , 0.9375],
[ 0. , 0.4375, 0.75 , 0.9375, 1. ]])
I thought you could do this with np.tril but it gives a result I do not expect.
mat = np.tril(x1, k = -1 )
print(mat)
what am I missing ?
I apologize in advance if this is a trivial question but I could not figure out how to it without looping.
You can do:
x = np.ones((5, 5), dtype=float)
x[np.triu_indices(5, 1)] = x1 # sets the upper triangle
x[np.triu_indices(5, 1)[::-1]] = x1 # sets the lower triangle
In the last line, the indices are reversed since your x1
is ordered for the upper triangle. You could also use x[np.tril_indices(5, -1)] = x1[::-1]
if that feels more intuitive.
You can use boolean indexing/mask
-
N = 5 # Output array length
out = np.eye(N) # Initialize output array with ones on diagonal
# Mask of upper triangular region except the diagonal region
range1 = np.arange(N)
mask = range1[:,None] < np.arange(N)
# Or simply: mask = np.triu(np.ones((N,N)),1)==1
# Insert x1's at upper diagonal region (except the diagonal) and paste
# transposed version of itself on lower diagonal region (including diagonal)
out[mask] = x1
out[~mask] = out.T[~mask]
Benchmarking
For the solutions posted so far and dealing with numpy arrays, here's a quick runtime test for the given inputs -
In [110]: %timeit triu_indices_based(x1,N)
10000 loops, best of 3: 19.9 µs per loop
In [111]: %timeit mask_based(x1,N)
100000 loops, best of 3: 6.88 µs per loop
With larger input array of x1
with 2001000
elements, here's the runtime results -
In [91]: %timeit mask_based(x1,N)
10 loops, best of 3: 34.9 ms per loop
In [92]: %timeit triu_indices_based(x1,N)
10 loops, best of 3: 80.9 ms per loop
I'm not sure if this is the formation rule you'd like, but you can do it with list comprehension:
x1 = np.array([0.9375, 0.75, 0.4375, 0.0, 0.9375, 0.75, 0.4375, 0.9375, 0.75, 0.9375])
x2 = [ [ x1[i-1-j] if j<i else x1[-i-1+j] for j in range(5) ] for i in range(5) ]
x2 = [ [ 1 if i==j else x2[i][j] for j in range(5) ] for i in range(5) ]
for el in x2:
print el
gives me
[1, 0.9375, 0.75, 0.4375, 0.0]
[0.9375, 1, 0.9375, 0.75, 0.4375]
[0.75, 0.9375, 1, 0.9375, 0.75]
[0.4375, 0.75, 0.9375, 1, 0.9375]
[0.0, 0.4375, 0.75, 0.9375, 1]