Вот один подход:
def subdiag_prod_(a):
sub_diag = np.diagonal(a, offset=-1)
mask = np.triu(np.ones((sub_diag.shape*2))).astype('bool')
m = sub_diag[:,None].T * mask
ma = np.ma.array(sub_diag[:,None].T * mask, mask=~mask)
diag = np.prod(ma, axis=1).data
out = np.diag(diag)
last_row = np.zeros([out.shape[0]+1]*2)
last_row[:out.shape[0], :out.shape[1]] += out
return last_row
a = np.random.randint(1,5,(10,10))
array([[2, 2, 1, 4, 3, 1, 3, 1, 4, 4],
[2, 2, 2, 1, 1, 2, 3, 2, 2, 2],
[4, 2, 2, 4, 2, 1, 3, 3, 3, 4],
[2, 3, 3, 1, 1, 1, 4, 2, 3, 4],
[3, 3, 1, 1, 2, 1, 3, 4, 4, 3],
[1, 4, 4, 1, 1, 4, 1, 1, 1, 4],
[4, 2, 3, 2, 1, 4, 4, 1, 3, 2],
[4, 2, 2, 4, 4, 4, 1, 4, 3, 1],
[1, 3, 1, 1, 2, 2, 2, 3, 4, 1],
[1, 3, 2, 2, 3, 4, 1, 3, 2, 1]])
subdiag_prod(a)
array([[288., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 144., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 72., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 24., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 24., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 24., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 6., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 6., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 2., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]])
Подробности
Первый шаг - взять поддиагональ ndarray с помощью np.diagonal
:
sub_diag = np.diagonal(a, offset=-1)
# array([2, 2, 3, 1, 1, 4, 1, 3, 2])
Мы можем создать mask
, используя np.tril
, который мы затем можем использовать, чтобы взять произведение субдиагональных элементов в указанном виде:
mask = np.triu(np.ones((sub_diag.shape*2))).astype('bool')
Теперь мы можем создать замаскированный массив, используя вышеуказанную ndarray
в качестве маски, умножив маску и субдиагональ:
mask = np.ma.array(sub_diag[:,None].T * mask, mask=~mask)
Теперь мы можем взять построчное произведение массива в маске:
d = np.prod(ma, axis=1).data
# array([288, 144, 72, 24, 24, 24, 6, 6, 2])
А затем просто построите из него матрицу цифон:
out = np.diag(d)
last_row = np.zeros([out.shape[0]+1]*2)
last_row[:out.shape[0], :out.shape[1]] += out