Получение диагоналей массива 2D numpy, влияющих на исходный массив - PullRequest
2 голосов
/ 06 августа 2020

У меня есть массив 2D numpy. Я хотел бы изменить его, создав массив его диагональных элементов и изменив диагональный массив, чтобы эти изменения отражались на исходном 2D-массиве.

Я пробовал с:

>>> a = np.ones(shape=(3,3))
>>> d1 = a[np.diag_indices_from(a)]
>>> d1
array([1., 1., 1.])
>>> d1[0] = 2
>>> d1
array([2., 1., 1.])
>>>a
array([[1., 1., 1.],
       [1., 1., 1.],
       [1., 1., 1.]])

Видно, что изменения не влияют на исходный массив.

Есть ли способ создать диагональный массив, который также повлияет на исходный 2D-массив?

РЕДАКТИРОВАТЬ: Я получаю эффект Ищу, когда работаю со строками или столбцами:

>>> row0 = a[0]
>>> row0[0] = 0
>>> a
array([[0., 1., 1.],
       [1., 1., 1.],
       [1., 1., 1.]])
>>> column0=a[:,0]
>>> column0[2]=3
>>> a
array([[0., 1., 1.],
       [1., 1., 1.],
       [3., 1., 1.]])

Ответы [ 5 ]

2 голосов
/ 06 августа 2020

Вы можете использовать np.einsum, чтобы получить именно то, что вы хотите:

.. versionadded :: 1.10.0

Views returned from einsum are now writeable whenever the input array
is writeable. For example, ``np.einsum('ijk...->kji...', a)`` will now
have the same effect as :py:func:`np.swapaxes(a, 0, 2) <numpy.swapaxes>`
and ``np.einsum('ii->i', a)`` will return a writeable view of the diagonal
of a 2D array.
a = np.ones((3,3))
b = np.einsum("ii->i",a)
b[:] = 2,3,4
a
# array([[2., 1., 1.],
#        [1., 3., 1.],
#        [1., 1., 4.]])
2 голосов
/ 06 августа 2020

Когда вы так индексируете ( причудливая индексация ), вы получаете копию, а не представление . Вам просто нужно присвоить d1 обратно a так же, как вы его определили

a = np.ones(shape=(3,3))
d1 = a[np.diag_indices_from(a)]
d1[0] = 2
a[np.diag_indices_from(a)] = d1
1 голос
/ 06 августа 2020

При базовой c индексации развернутой версии массива это возможно:

In [150]: a = np.ones((3,3))                                                                         
In [151]: d1 = a.ravel()[::4]                                                                        
In [152]: d1[0] *= 2                                                                                 
In [153]: a                                                                                          
Out[153]: 
array([[2., 1., 1.],
       [1., 1., 1.],
       [1., 1., 1.]])
In [154]: d1[:] = [1,2,3]                                                                            
In [155]: a                                                                                          
Out[155]: 
array([[1., 1., 1.],
       [1., 2., 1.],
       [1., 1., 3.]])
   

np.diag вроде как делает это, но устанавливает результат только для чтения.

In [157]: np.diag(a)                                                                                 
Out[157]: array([1., 2., 3.])
In [158]: d2 = np.diag(a)                                                                            
In [159]: d2[1] = 0                                                                                  
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-159-64d5edb77b4c> in <module>
----> 1 d2[1] = 0

ValueError: assignment destination is read-only
In [160]: a[1,1] = 10                                                                                
In [161]: a                                                                                          
Out[161]: 
array([[ 1.,  1.,  1.],
       [ 1., 10.,  1.],
       [ 1.,  1.,  3.]])
In [162]: d2                                                                                         
Out[162]: array([ 1., 10.,  3.])
In [163]: d1                                                                                         
Out[163]: array([ 1., 10.,  3.])

Изменение a изменяет d2, но мы не можем сделать это наоборот.

1 голос
/ 06 августа 2020

Правильный способ сделать это - использовать np.diagonal и np.fill_diagonal. Существует способ скрутить numpy, который также добавлен ниже:

a = np.ones(shape=(3,3))
d1 = np.diagonal(a).copy()
d1[0] = 2
np.fill_diagonal(a,d1)

вывод:

a

[[2. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]

Обратите внимание, что на основе numpy do c для np.diagonal: Начиная с NumPy 1.9, он возвращает доступное только для чтения представление исходного массива. Попытка записи в результирующий массив приведет к ошибке. В некоторых будущих выпусках он вернет представление для чтения / записи, а запись в возвращенный массив изменит исходный массив. Возвращаемый массив будет иметь тот же тип, что и входной массив.

Чтобы скрутить numpy, вы можете это сделать, но я бы предложил использовать вышеуказанное решение:

a = np.ones(shape=(3,3))
d1 = np.diagonal(a)
d1.setflags(True)
d1[0] = 2
np.fill_diagonal(a,d1)

вывод:

a

[[2. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]
0 голосов
/ 06 августа 2020

Боюсь, что нет. Массив NumPy - это объект класса с собственным интерфейсом индексации. Когда вы создаете диагональ (вектор) как отдельный объект, вы нарушаете эту исходную парадигму индексирования и заменяете ее другой - вектор является отдельным объектом.

Если вам нужна эта функция, вы мог бы написать свой собственный класс, создав новый объект element для каждого элемента матрицы. Затем вы можете легко изменить эти элементы (с помощью методов класса), изменив значение элемента, но сохранив экземпляр элемента нетронутым.

Таким образом, всякий раз, когда вы извлекаете диагональ - или любой другой фрагмент матрицы, ссылка, et c. - вы работаете с исходным объектом, а не с неизменяемым значением.

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