Convert 1d array to lower triangular matrix

Clash Royale CLAN TAG#URR8PPPConvert 1d array to lower triangular matrix
I would like to convert a 1 dimensional array into a lower, zero diagonal matrix while keeping all the digits.
I am aware of numpy.tril function but it replaces some of the elements with zeros. I need to expand the matrix to contain all the original digits.
numpy.tril
For example:
[10,20,40,46,33,14,12,46,52,30,59,18,11,22,30,2,11,58,22,72,12]
Should be
0
10 0
20 40 0
46 33 14 0
12 46 52 30 0
59 18 11 22 30 0
2 11 58 22 72 12 0
3 Answers
3
With the input array holding all the values as required to fill up the lower diagonal places, here's one approach with masking -
masking
def fill_lower_diag(a):
n = int(np.sqrt(len(a)*2))+1
mask = np.tri(n,dtype=bool, k=-1) # or np.arange(n)[:,None] > np.arange(n)
out = np.zeros((n,n),dtype=int)
out[mask] = a
return out
Sample run -
In [82]: a
Out[82]:
array([10, 20, 40, 46, 33, 14, 12, 46, 52, 30, 59, 18, 11, 22, 30, 2, 11,
58, 22, 72, 12])
In [83]: fill_lower_diag(a)
Out[83]:
array([[ 0, 0, 0, 0, 0, 0, 0],
[10, 0, 0, 0, 0, 0, 0],
[20, 40, 0, 0, 0, 0, 0],
[46, 33, 14, 0, 0, 0, 0],
[12, 46, 52, 30, 0, 0, 0],
[59, 18, 11, 22, 30, 0, 0],
[ 2, 11, 58, 22, 72, 12, 0]])
Timings on large array with 5k x 5k shape -
5k x 5k
In [146]: np.random.seed(0)
In [147]: n = 5000
In [148]: a = np.random.randint(0,9,n*(n+1)/2)
In [149]: %timeit tril_indices_app(a) #@Brenlla's solution
1 loop, best of 3: 218 ms per loop
In [151]: %timeit fill_lower_diag(a) # From this post
10 loops, best of 3: 43.1 ms per loop
k
@Brenlla I think its also about generating those indices vs getting the mask that showing the difference, specially with decent to large sized arrays.
– Divakar
Jul 20 at 10:29
Yes, that's true. In your timing code shoudn't it be
n*(n-1)?– Brenlla
Jul 20 at 10:39
n*(n-1)
@Brenlla Ah yes thanks. It would generate 5001 x 5001 shaped array. Timings shouldn't change much.
– Divakar
Jul 20 at 10:42
You could also use the numpy function np.tril_indices:
np.tril_indices
arr = np.array([10,20,40,46,33,14,12,46,52,30,59,18,11,22,30,2,11,58,22,72,12])
n = int(np.sqrt(len(arr)*2))+1
Generate zeros matrix and fill the lower part with your values:
idx = np.tril_indices(n, k=-1, m=n)
matrix = np.zeros((n,n)).astype(int)
matrix[idx] = arr
array([[ 0, 0, 0, 0, 0, 0, 0],
[10, 0, 0, 0, 0, 0, 0],
[20, 40, 0, 0, 0, 0, 0],
[46, 33, 14, 0, 0, 0, 0],
[12, 46, 52, 30, 0, 0, 0],
[59, 18, 11, 22, 30, 0, 0],
[ 2, 11, 58, 22, 72, 12, 0]])
You can also try the below function to get the list.
import pprint
def get_triangled_list(l, rows, typ='lower'):
if type(l) is not list:
print 'First parameter should be a list'
return None
if type(rows) is not int:
print 'Second parameter should be a list'
return None
if not(typ == 'lower' or typ == 'upper'):
print 'ERROR:', typ, 'is not allowed type'
return None
new_l =
length = len(l)
num_items = ((rows-1) * rows)/ 2
if length != num_items:
print 'ERROR: ', 'There should be exactly', num_items, 'items for ', rows, 'rows, found', length, 'items.'
return None
if typ == 'upper':
for i in range(rows):
temp_l = [0]*(i+1) + [l.pop(0) for j in range(7-(i+1))]
new_l.append(temp_l)
elif typ=='lower':
for i in range(rows):
temp_l = [l.pop(0) for j in range(i)] + [0]*(rows-i)
new_l.append(temp_l)
return new_l
if __name__ == '__main__':
l = [10,20,40,46,33,14,12,46,52,30,59,18,11,22,30,2,11,58,22,72,12]
# TEST CASE 1 (LOWER TRIANGLE, default)
new_lower = get_triangled_list(l, 7)
pprint.pprint(new_lower)
print('n')
# TEST CASE 2 (UPPER TRIANGLE, passing one more parameter)
l = [10,20,40,46,33,14,12,46,52,30,59,18,11,22,30,2,11,58,22,72,12]
new_upper = get_triangled_list(l, 7, 'upper')
pprint.pprint(new_upper)
[[0, 0, 0, 0, 0, 0, 0],
[10, 0, 0, 0, 0, 0, 0],
[20, 40, 0, 0, 0, 0, 0],
[46, 33, 14, 0, 0, 0, 0],
[12, 46, 52, 30, 0, 0, 0],
[59, 18, 11, 22, 30, 0, 0],
[2, 11, 58, 22, 72, 12, 0]]
[[0, 10, 20, 40, 46, 33, 14],
[0, 0, 12, 46, 52, 30, 59],
[0, 0, 0, 18, 11, 22, 30],
[0, 0, 0, 0, 2, 11, 58],
[0, 0, 0, 0, 0, 22, 72],
[0, 0, 0, 0, 0, 0, 12],
[0, 0, 0, 0, 0, 0, 0]]
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Yep, mask indexing can be much faster and more memory efficient (provided you are indexing enough elements). My aproach would only be competitive for a small
krelative to the triangular matrix size– Brenlla
Jul 20 at 10:19