a.transpose (). ravel () [0] = x не может изменить значение исходного массива в numpy? - PullRequest
0 голосов
/ 13 января 2019

среда: Python 3.6.0 | Anaconda custom (64-bit), numpy версия: 1.11.3
Пример:

In[1]: import numpy as np
In[2]: a = np.array([[1,2,3], [4,5,6]])
In[3]: a
Out[4]: 
array([[1, 2, 3],
       [4, 5, 6]])
In[5]: a.transpose()[0] = -1
In[6]: a
Out[6]: 
array([[-1,  2,  3],
       [-1,  5,  6]])
In[7]: a.ravel()[0] = -2 
In[8]: a
Out[8]: 
array([[-2,  2,  3],
       [-1,  5,  6]])
In[9]: a.transpose().ravel()[0] = -3
In[10]: a
Out[10]: 
array([[-2,  2,  3],
       [-1,  5,  6]])

Я знаю, transpose() и ravel() возвращают представление массива, поэтому мы можем изменить его значение исходного массива. Однако когда мы используем transpose().ravel(), мы не можем его изменить? Зачем?

Ответы [ 2 ]

0 голосов
/ 13 января 2019
In [382]: a = np.array([[1,2,3], [4,5,6]])
In [383]: a
Out[383]: 
array([[1, 2, 3],
       [4, 5, 6]])
In [384]: a.ravel()
Out[384]: array([1, 2, 3, 4, 5, 6])

ravel дает 1-мерное представление массива и показывает значения в порядке их появления в буфере данных.

In [385]: a.T
Out[385]: 
array([[1, 4],
       [2, 5],
       [3, 6]])
In [386]: a.T.ravel()
Out[386]: array([1, 4, 2, 5, 3, 6])

ravel транспонирования показывает элементы в другом порядке - если только мы не указываем порядок как 'F' (или 'K').

In [387]: a.T.ravel(order='F')
Out[387]: array([1, 2, 3, 4, 5, 6])

ravel (и другие операции) создает view, если массив может использовать исходные данные, с изменением только shape и strides. Если он не может, он должен сделать копию.

Из-за этого изменения порядка элементов при транспонировании, индексировании с чем-то отличным от [0], выбирается другое значение:

In [397]: a.ravel()[3]
Out[397]: 4               # -1 in your Out[8]
In [398]: a.T.ravel()[3]
Out[398]: 5

Вы видите некоторую двусмысленность, когда вы просите изменить 4-й элемент транспонирования. Он может отличаться в зависимости от того, как вы проходите элементы.

0 голосов
/ 13 января 2019

ravel возвращает копию, а не представление

Из numpy.ravel документов :

Возвращается одномерный массив, содержащий элементы ввода. Копия производится только при необходимости.

Так что, по сути, когда вы рассказываете о транспонировании, на самом деле нужна копия. Вы изменяете значение в копии, поэтому оно не отражается в исходном массиве.

Проверка, является ли возвращенный массив представлением или копией

В таком простом случае, как этот, вы можете проверить, является ли массив b представлением a или нет, сравнив идентичность b.base и a:

a = np.array([[1,2,3], [4,5,6]])
b = a.T
c = b.ravel()

print('b is a view of a\n%s\n' % (b.base is a))
print('c is a view of a\n%s\n' % (c.base is a))

Выход:

b is a view of a
True

c is a view of a
False

Почему a.T.ravel() возвращает копию?

Shocker: на самом деле есть способ заставить a.T.ravel() возвращать представление вместо копии. Вы можете сделать это, явно установив order='F' (то есть порядок Fortran):

a = np.array([[1,2,3], [4,5,6]])
c = a.T.ravel()
d = a.T.ravel(order='F')

print('d is a view of a\n%s\n' % (d.base is a))

Выход:

d is a view of a
True

Однако, изменение значения order kwarg изменит порядок (кажется) значений в массиве raveled:

print('c\n%s\n' % c)
print('d\n%s\n' % d)

Выход:

c
[1 4 2 5 3 6]

d
[1 2 3 4 5 6]

Чтобы понять, почему изменение order приводит к возвращению представления, мы можем взглянуть на код самой функции ravel. Реализация np.ndarray.ravel - это , похороненная в слое C . Читая об этом источник, становится ясно, что для возврата вида из ravel должны быть выполнены два условия:

  • Входной массив должен быть смежным.

  • Порядок непрерывного входного массива должен соответствовать порядку order kwarg, переданного в ravel.

Кварг имеет значение по умолчанию order='C'. Таким образом, по умолчанию ravel будет возвращать представление только в том случае, если вы запускаете его в C-смежном массиве. В большинстве случаев, когда вы инициализируете новый массив Numpy a, он будет начинаться как C-смежный. Однако транспонирование a.T будет F-смежным. Вы можете увидеть это в действии в своем коде, проверив .flags свойство ваших массивов:

a = np.array([[1,2,3], [4,5,6]])

print('the flags of a\n%s\n' % a.flags)
print('the flags of a.T\n%s\n' % a.T.flags)

Выход:

the flags of a
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY : False

the flags of a.T
  C_CONTIGUOUS : False
  F_CONTIGUOUS : True
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY : False

Что, черт возьми, означают С- и F-смежные?

Есть большая вероятность, что термины C-смежный и F-смежный кажутся вам бредом. Их объяснение потребовало бы совершенно другого вопроса, который, к счастью, кто-то из SO уже задал. Вот ссылка на старый ответ , который дает действительно интуитивно понятный обзор того, что на самом деле означает порядок C и F.

Протест

В вашем реальном коде я бы не слишком беспокоился о том, является ли ravel возвращаемыми представлениями или копиями. В действительности вы не всегда получаете повышение производительности, гарантируя использование представлений. Избегайте преждевременной оптимизации в целом.

...