Преобразование 1-го массива в нижнюю треугольную матрицу
Я хотел бы преобразовать 1-мерный массив в нижнюю, нулевую диагональную матрицу, сохраняя все цифры.
Я знаю о функции numpy.tril
, но он заменяет некоторые элементы нулями. Мне нужно расширить матрицу, чтобы содержать все исходные цифры.
Например:
[10,20,40,46,33,14,12,46,52,30,59,18,11,22,30,2,11,58,22,72,12]
Должно быть
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
Ответы
Ответ 1
Когда входной массив содержит все значения, необходимые для заполнения нижних диагональных мест, здесь один подход с 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
Пример прогона -
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]])
Сроки на большом массиве с формой 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 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
Ответ 2
Вы также можете использовать функцию numpy 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
Создайте матрицу нулей и заполните нижнюю часть вашими значениями:
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]])
Ответ 3
Вы также можете попробовать следующую функцию, чтобы получить список.
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]]