Почему numpy.ndarray.view игнорирует предыдущий вызов numpy.ndarray.newbyteorder?

У меня есть массив NumPy с одним элементом типа данных uint32:

>>> import numpy as np
>>> a = np.array([123456789], dtype=np.uint32)
>>> a.dtype.byteorder
'='

Затем я могу интерпретировать данные как прямой порядок байтов:

>>> a.newbyteorder("<").dtype.byteorder
'<'
>>> a.newbyteorder("<")
array([123456789], dtype=uint32)

Или с прямым порядком байтов:

>>> a.newbyteorder(">").dtype.byteorder
'>'
>>> a.newbyteorder(">")
array([365779719], dtype=uint32)

Если последний возвращает другое число 365779719, поскольку моя платформа является прямым порядком байтов - и, следовательно, была записана в память в порядке обратного порядка байтов.

Что для меня неожиданно, так это то, что следующий добавленный вызов view, похоже, не зависит от этой интерпретации:

>>> a.newbyteorder("<").view(np.uint8)
array([ 21, 205,  91,   7], dtype=uint8)
>>> a.newbyteorder(">").view(np.uint8)
array([ 21, 205,  91,   7], dtype=uint8)

Я ожидал, что числа будут обратными для порядка байтов с прямым порядком байтов. Почему этого не происходит? Разве view данные не просматриваются "через" метод newbyteorder?

Кстати: если я использую byteswap вместо newbyteorder и, следовательно, копирую и изменяю байты в памяти, я, очевидно, получаю желаемый результат:

>>> a.byteswap("<").view(np.uint8)
array([ 21, 205,  91,   7], dtype=uint8)
>>> a.byteswap(">").view(np.uint8)
array([  7,  91, 205,  21], dtype=uint8)

Однако я не хочу копировать данные.


person finefoot    schedule 20.05.2018    source источник
comment
Метод From newbyteorder для массива говорит, что он эквивалентен представлению с другим dtype (см. Его документацию).   -  person hpaulj    schedule 21.05.2018
comment
@hpaulj Да, я тоже читал об этом в другом ответе ниже. Однако на самом деле это не помогает, потому что я думал, что также возможны два последовательных вызова view.   -  person finefoot    schedule 21.05.2018


Ответы (3)


In [280]: a = np.array([123456789, 234567891, 345678912], dtype=np.uint32)

In [282]: a.tobytes()
Out[282]: b'\x15\xcd[\x07\xd38\xfb\r@\xa4\x9a\x14'

In [284]: a.view('uint8')
Out[284]: 
array([ 21, 205,  91,   7, 211,  56, 251,  13,  64, 164, 154,  20],
      dtype=uint8)

Это то же самое, что и a.view('<u1') и a.view('>u1'), поскольку окончание не имеет значения для отдельных байтов.

In [291]: a.view('<u4')
Out[291]: array([123456789, 234567891, 345678912], dtype=uint32)
In [292]: a.view('>u4')
Out[292]: array([ 365779719, 3543726861, 1084529172], dtype=uint32)

Представление полностью зависит от данных, а не от текущего (последнего) представления:

In [293]: a.view('<u4').view('u1')
Out[293]: 
array([ 21, 205,  91,   7, 211,  56, 251,  13,  64, 164, 154,  20],
      dtype=uint8)
In [294]: a.view('>u4').view('u1')
Out[294]: 
array([ 21, 205,  91,   7, 211,  56, 251,  13,  64, 164, 154,  20],
      dtype=uint8)

Об идее изменения формы и поворота вспять:

In [295]: a.view('u1').reshape(-1,4)
Out[295]: 
array([[ 21, 205,  91,   7],
       [211,  56, 251,  13],
       [ 64, 164, 154,  20]], dtype=uint8)
In [296]: a.view('u1').reshape(-1,4)[:,::-1]
Out[296]: 
array([[  7,  91, 205,  21],
       [ 13, 251,  56, 211],
       [ 20, 154, 164,  64]], dtype=uint8)

Но я не могу изменить представление (на u4) этого массива, потому что он не является непрерывным:

In [297]: a.view('u1').reshape(-1,4)[:,::-1].view('<u4')
....
ValueError: To change to a dtype of a different size, the array must be C-contiguous

Посмотрите еще немного на свойства этого перевернутого массива:

In [298]: a1 = a.view('u1').reshape(-1,4)[:,::-1]
In [299]: a1.flags
Out[299]: 
  C_CONTIGUOUS : False
  F_CONTIGUOUS : False
  ....
In [300]: a1.strides             # reversing is done with strides
Out[300]: (4, -1)

Два массива используют один и тот же буфер данных. a2 просто начинается с другого байта:

In [301]: a.__array_interface__['data']
Out[301]: (32659520, False)
In [302]: a1.__array_interface__['data']
Out[302]: (32659523, False)

Я не могу изменить форму a1 на месте:

In [304]: a1.shape = (12,)
...
AttributeError: incompatible shape for a non-contiguous array

Если я сделаю reshape, я получу копию (как показывает совершенно другой адрес буфера данных):

In [305]: a2 = a1.reshape(-1)
In [306]: a2
Out[306]: 
array([  7,  91, 205,  21,  13, 251,  56, 211,  20, 154, 164,  64],
      dtype=uint8)
In [307]: a2.view('<u4')
Out[307]: array([ 365779719, 3543726861, 1084529172], dtype=uint32)
In [308]: a2.__array_interface__['data']
Out[308]: (37940512, False)

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


newbyteorder в документах говорится, что это эквивалентно:

arr.view(arr.dtype.newbytorder(new_order))

Итак, a.view('<u4').newbyteorder('>') это то же самое, что a.view('<u4'). Ни одного из этих изменений a.

person hpaulj    schedule 23.05.2018

Новый порядок байтов, применяемый с newbyteorder, является исключительно свойством dtype массива; a.newbyteorder("<") возвращает представление a с прямым порядком байтов dtype. Он не меняет содержимое памяти и не влияет на форму или шаги массива.

ndarray.view не заботится о dtype исходного массива, little-endian или big. Он заботится о форме массива, шагах и фактическом содержимом памяти, ни одно из которых не изменилось.

person user2357112 supports Monica    schedule 21.05.2018
comment
Значит, я никогда не смогу объединить два вызова view? (Или комбинируйте newbyteorder и view, если на то пошло.) Как же мне получить uint8 фрагменты в порядке прямого байта без изменения памяти? - person finefoot; 21.05.2018
comment
@Jayjayyy: На одно значение uint32? view и срез-реверс. Для массива из более чем одного uint32? Вам нужно будет либо добавить новое измерение, либо изменить содержимое памяти; иначе не получится. - person user2357112 supports Monica; 21.05.2018
comment
Хорошо, поэтому я не могу заставить метод view видеть данные с прямым порядком байтов. Это ограничение текущей реализации в NumPy или это вообще невозможно? Что вы имеете в виду под view и срезом-реверсом? Что вы имеете в виду под добавлением нового измерения? - person finefoot; 21.05.2018
comment
@Jayjayyy Попробуйте np.sum(a.newbyteorder('<')), а также измените знак (в качестве альтернативы попробуйте a.newbyteorder('<').tolist()). Итак, мой ответ на Как мне получить фрагменты uint8 в порядке обратного порядка байтов без изменения памяти, тогда? будет заключаться в том, что вы можете сделать это, получив доступ к элементам в вид. - person AGN Gazer; 21.05.2018
comment
@ user2357112 Вы имеете в виду это? Когда у меня будет a = np.array([123456789, 234567891, 345678912], dtype=np.uint32), тогда я смогу использовать a.view(np.uint8).reshape(-1, 4)[:, ::-1]? Это без изменения памяти и без копирования массива? - person finefoot; 23.05.2018

Просто чтобы добавить в ответ @ user2357112, из документации:

Как вы можете понять из введения, есть два способа повлиять на взаимосвязь между порядком байтов в массиве и базовой памятью, на которую он смотрит:

  • Измените информацию о порядке байтов в массиве dtype, чтобы он интерпретировал базовые данные как находящиеся в другом порядке байтов. Это роль arr.newbyteorder()
  • Измените порядок байтов базовых данных, оставив интерпретацию dtype без изменений. Это то, что делает arr.byteswap().

Мое ударение в цитате выше.


Другая мысль собрана из комментариев:

Поскольку newbyteorder () похож на view () в том, что он просто изменяет интерпретацию базовых данных без изменения данных, похоже, что представление в представлении является представлением тех же (исходных) данных. Итак, да, вы не можете «связать» представления (ну, вы можете ... но это всегда представление одних и тех же исходных данных).

Как тогда получить uint8 фрагменты в порядке прямого байта без изменения памяти?

Попробуйте np.sum(a.newbyteorder('<')) (как вариант, попробуйте a.newbyteorder('<').tolist()), а также измените знак / порядок следования байтов. Итак, мой ответ на вышеупомянутый вопрос будет заключаться в том, что вы не можете этого сделать: либо память изменяется «на месте» с помощью byteswap(), либо путем копирования данных в новую ячейку памяти при доступе к элементам в представлении.

person AGN Gazer    schedule 21.05.2018
comment
Да, это то, что я прочитал, поэтому я подумал, что хочу интерпретировать базовые данные как находящиеся в другом порядке байтов, а затем разделить их (это вновь интерпретированные данные) на uint8 фрагменты. Однако интерпретация не влияет на последовательный вызов _2 _... - person finefoot; 21.05.2018
comment
@Jayjayyy Поскольку newbyteorder() похож на view() в том, что он просто изменяет интерпретацию базовых данных без изменения этих данных, похоже, что представление в представлении является представлением тех же (исходных) данных. Итак, да, вы не можете связать представления (ну, вы можете, но это всегда представление одних и тех же исходных данных). - person AGN Gazer; 21.05.2018