Создать диагональную матрицу из столбца массива с несколькими фигурами - PullRequest
4 голосов
/ 01 апреля 2020

Предположим, у меня есть массив типа

mat = np.array([[1,1],[2,2],[3,3]])

, имеющий форму (3,2). Я хочу создать новый массив с формой (3,2,2), взяв последнюю ось массива и создав из нее диагональные матрицы. Я могу сделать это с помощью для l oop, как

mat2 = np.zeros((3,2,2))
for i in np.arange(0,3):
    mat2[i] = np.diag(mat[i])

, что дает желаемый результат

[[[1. 0.]
  [0. 1.]]

 [[2. 0.]
  [0. 2.]]

 [[3. 0.]
  [0. 3.]]]

Но есть ли способ сделать это в прямом векторизации (быстрее? !) версия? В моей реальной задаче у меня есть большой многомерный массив формы (..., n), и мне нужно преобразовать последнюю ось в диагональную матрицу с формой (..., n, n) в конце.

Ответы [ 4 ]

3 голосов
/ 01 апреля 2020

Здесь возможная реализация: используйте np.diagonal , чтобы рассмотреть соответствующие диагонали и заставить запись быть доступной для записи с помощью setflags, и записать в представление:

expanded = np.zeros(mat.shape + mat.shape[-1:], dtype=mat.dtype)
diagonals = np.diagonal(expanded, axis1=-2, axis2=-1)
diagonals.setflags(write=True)

diagonals[:] = mat

expanded
array([[[1, 0],
        [0, 1]],

       [[2, 0],
        [0, 2]],

       [[3, 0],
        [0, 3]]])
2 голосов
/ 01 апреля 2020

Существует замечательная библиотека под названием numba, которая позволяет своевременно компилировать python функции для получения скорости, сравнимой с C / Fortran при работе с массивами numpy. Если вы находитесь в точке, где вы не можете express векторизовать свою проблему, это может спасти ваш день.

from numba import jit

@jit(nopython=True)
def get_diag_mat(mat):
    mat2 = np.zeros((mat.shape[0], mat.shape[1], mat.shape[1]))
    for i in range(mat2.shape[0]):
        mat2[i] = np.diag(mat[i])
    return mat2

Даже если вы можете векторизовать вашу проблему с помощью numpy, иногда вы можете сэкономить память, когда Вы переключаетесь на циклы "jitted".

PS: при первом выполнении функции возникают накладные расходы из-за компиляции.

PPS: обратите внимание, что вы редко хотите использовать np.arange. Особенно не для зацикливания. python3 s range является ленивым и не создает массив целых чисел в вашей памяти.

0 голосов
/ 01 апреля 2020
In [219]: m = mat.shape[-1]                                                                    
In [220]: mat2 = np.zeros(mat.shape+(m,),mat.dtype)                                            
In [221]: idx = np.arange(m)                                                                   
In [222]: mat2[...,idx,idx] = mat[...,idx]                                                     
In [223]: mat2                                                                                 
Out[223]: 
array([[[1, 0],
        [0, 1]],

       [[2, 0],
        [0, 2]],

       [[3, 0],
        [0, 3]]])

тестирование на большем mat:

In [224]: mat = np.array([[1,2,3],[2,3,4],[3,4,5]])                                            
In [225]: m = mat.shape[-1]                                                                    
In [226]: mat2 = np.zeros(mat.shape+(m,),mat.dtype)                                            
In [227]: idx = np.arange(m)                                                                   
In [228]: mat2[...,idx,idx] = mat[...,idx]                                                     
In [230]: mat2                                                                                 
Out[230]: 
array([[[1, 0, 0],
        [0, 2, 0],
        [0, 0, 3]],

       [[2, 0, 0],
        [0, 3, 0],
        [0, 0, 4]],

       [[3, 0, 0],
        [0, 4, 0],
        [0, 0, 5]]])
0 голосов
/ 01 апреля 2020

Вы можете использовать функцию np.eye и умножить.

>>> mat = np.array([[1,1],[2,2],[3,3]])
>>> np.multiply(np.eye(2),mat[:,np.newaxis])
array([[[ 1.,  0.],
        [ 0.,  1.]],

       [[ 2.,  0.],
        [ 0.,  2.]],

       [[ 3.,  0.],
        [ 0.,  3.]]])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...