Numpy astype "upcasting" array вместо применения dtypes к столбцам - PullRequest
0 голосов
/ 30 апреля 2018

У меня есть двумерный массив Numpy, и я хотел бы применить определенный dtype к каждому столбцу.

a = np.arange(25).reshape((5,5))

In [40]: a
Out[40]: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24]])

In [41]: a.astype(dtype=[('width', '<i4'), ('height', '<i4'), ('depth', '<i4'), ('score', '<f4'), ('auc', '<f4')])

Я ожидал, что строка 41 применит dtype, который я желал, но вместо этого он "выскочил", создав новую ось, реплицируя весь массив один раз для каждого из dtypes:

Out[41]: 
array([[(0, 0, 0, 0.0, 0.0), (1, 1, 1, 1.0, 1.0), (2, 2, 2, 2.0, 2.0),
        (3, 3, 3, 3.0, 3.0), (4, 4, 4, 4.0, 4.0)],
       [(5, 5, 5, 5.0, 5.0), (6, 6, 6, 6.0, 6.0), (7, 7, 7, 7.0, 7.0),
        (8, 8, 8, 8.0, 8.0), (9, 9, 9, 9.0, 9.0)],
       [(10, 10, 10, 10.0, 10.0), (11, 11, 11, 11.0, 11.0),
        (12, 12, 12, 12.0, 12.0), (13, 13, 13, 13.0, 13.0),
        (14, 14, 14, 14.0, 14.0)],
       [(15, 15, 15, 15.0, 15.0), (16, 16, 16, 16.0, 16.0),
        (17, 17, 17, 17.0, 17.0), (18, 18, 18, 18.0, 18.0),
        (19, 19, 19, 19.0, 19.0)],
       [(20, 20, 20, 20.0, 20.0), (21, 21, 21, 21.0, 21.0),
        (22, 22, 22, 22.0, 22.0), (23, 23, 23, 23.0, 23.0),
        (24, 24, 24, 24.0, 24.0)]], 
      dtype=[('width', '<i4'), ('height', '<i4'), ('depth', '<i4'), ('score', '<f4'), ('auc', '<f4')])

Почему это произошло, учитывая, что число dtypes совпадает с числом столбцов (и поэтому я не ожидал, что апскейлинг)?

Как я могу взять существующий массив в памяти и применить dtypes для каждого столбца, как я и предполагал в строке 41? Спасибо.

Ответы [ 3 ]

0 голосов
/ 30 апреля 2018

Как правильно указывает @ senderle , вам редко требуется view, но вот возможное решение сделать это практически на месте, просто для удовольствия. Единственное изменение, которое вам нужно будет сделать, - убедиться, что все ваши типы имеют одинаковый размер.

a = np.arange(25, dtype='<i4').reshape((5,5))
b = a.view(dtype=[('width', '<i4'), ('height', '<i4'), ('depth', '<i4'), ('score', '<f4'), ('auc', '<f4')])
b['score'] = a[:, -2, np.newaxis].astype('<f4')
b['auc'] = a[:, -1, np.newaxis].astype('<f4')

Если мы собираемся делать нерекомендованные вещи, вы также можете вставить строку b.shape = (5,) после получения представления, чтобы исключить дополнительное измерение, сохраненное из a, и сделать нижеприведенные назначения более простыми.

Это даст вам представление b, которое обладает всеми желаемыми свойствами, но, конечно, испортит содержимое a:

>>> a
array([[         0,          1,          2, 1077936128, 1082130432],
       [         5,          6,          7, 1090519040, 1091567616],
       [        10,         11,         12, 1095761920, 1096810496],
       [        15,         16,         17, 1099956224, 1100480512],
       [        20,         21,         22, 1102577664, 1103101952]])
>>> b
array([[( 0,  1,  2,  3.,  4.)],
       [( 5,  6,  7,  8.,  9.)],
       [(10, 11, 12, 13., 14.)],
       [(15, 16, 17, 18., 19.)],
       [(20, 21, 22, 23., 24.)]],
      dtype=[('width', '<i4'), ('height', '<i4'), ('depth', '<i4'), ('score', '<f4'), ('auc', '<f4')])
0 голосов
/ 30 апреля 2018

Вот обходной путь, используя np.rec.fromarrays:

>>> dtype = [('width', '<i4'), ('height', '<i4'), ('depth', '<i4'), ('score', '<f4'), ('auc', '<f4')]
>>> np.rec.fromarrays(a.T, dtype=dtype)
rec.array([( 0,  1,  2,  3.,  4.), ( 5,  6,  7,  8.,  9.),
           (10, 11, 12, 13., 14.), (15, 16, 17, 18., 19.),
           (20, 21, 22, 23., 24.)],
          dtype=[('width', '<i4'), ('height', '<i4'), ('depth', '<i4'), ('score', '<f4'), ('auc', '<f4')])

Это recarray, но мы можем привести к ndarray, если это будет необходимо. Кроме того, dtype равен np.record, нам нужно (view-) привести его к void, чтобы получить «чистый» результат с нулевыми значениями.

>>> np.asarray(np.rec.fromarrays(a.T, dtype=dtype)).view(dtype)
array([( 0,  1,  2,  3.,  4.), ( 5,  6,  7,  8.,  9.),
       (10, 11, 12, 13., 14.), (15, 16, 17, 18., 19.),
       (20, 21, 22, 23., 24.)],
      dtype=[('width', '<i4'), ('height', '<i4'), ('depth', '<i4'), ('score', '<f4'), ('auc', '<f4')])
0 голосов
/ 30 апреля 2018

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

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

Почему предел?

Я полагаю, что формы назначения для структурированных массивов ограничены, потому что «столбцы» структурированных массивов не очень похожи на столбцы обычных 2-мерных массивов. Фактически, имеет смысл думать о структурированном массиве из десяти строк с тремя столбцами как о 1-d массиве из десяти экземпляров атомарного типа строки.

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

Как создать структурированное представление существующего массива

Честно говоря, я не знаю! Я обновлю этот ответ, если найду хороший способ. Но я не думаю, что найду хороший способ, потому что, как обсуждалось выше, структурированные скаляры имеют свою собственную отличительную структуру памяти. Можно что-то взломать с помощью буфера, который имеет правильную компоновку, но для этого нужно копаться во numpy внутренностях, что не идеально. При этом см. этот ответ от Безумного Физика, который сделал это несколько более элегантно, чем я думал, было возможно.

Стоит также отметить, что astype создает копию по умолчанию . Вы можете передать copy=False, но numpy может сделать копию, если определенные требования не будут выполнены.

Альтернативы ...

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

>>> a = np.array([(1, 2), (3, 4)], dtype=[('x', 'f8'), ('y', 'i8')])
>>> a
array([(1., 2), (3., 4)], dtype=[('x', '<f8'), ('y', '<i8')])

Теперь, если мы попытаемся присвоить обычный 2-й массив массиву a, мы получим ошибку:

>>> a[:] = np.array([[11, 22], [33, 44]])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: could not broadcast input array from shape (2,2) into shape (2)

Но мы можем легко назначить по столбцу:

>>> a['x'] = [11, 22]
>>> a['y'] = [33, 44]
>>> a
array([(11., 33), (22., 44)], dtype=[('x', '<f8'), ('y', '<i8')])

Мы также можем использовать Python кортежи . Это перезаписывает весь массив:

>>> a[:] = [(111, 222), (333, 444)]
>>> a
array([(111., 222), (333., 444)], dtype=[('x', '<f8'), ('y', '<i8')])

Мы также можем присваивать данные построчно, используя кортежи:

>>> a[1] = (3333, 4444)
>>> a
array([( 111.,  222), (3333., 4444)], dtype=[('x', '<f8'), ('y', '<i8')])

Опять же, это терпит неудачу, если мы пытаемся передать список или массив:

>>> a[1] = [3333, 4444]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: setting an array element with a sequence.
>>> a[1] = np.array([3333, 4444])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: setting an array element with a sequence.

Наконец, обратите внимание, что мы видим то же поведение, которое вы видели с astype, когда мы пытаемся создать структурированный массив из вложенных списков или numpy массивов. numpy просто передает входной массив по типу данных, создавая массив структурированных скаляров 2-d :

>>> a
array([[(1., 1), (2., 2)],
       [(3., 3), (4., 4)]], dtype=[('x', '<f8'), ('y', '<i8')])
>>> a = np.array(np.array([[1, 2], [3, 4]]), dtype=[('x', 'f8'), ('y', 'i8')])
>>> a
array([[(1., 1), (2., 2)],
       [(3., 3), (4., 4)]], dtype=[('x', '<f8'), ('y', '<i8')])

Если ваша цель - просто создать новый массив, посмотрите ответы на на этот вопрос . Они охватывают несколько полезных подходов, включая numpy.core.records.fromarrays и numpy.core.records.fromrecords. См. Также ответ Пола Панцера , в котором рассказывается, как создать новый массив записей (структурированный массив, обеспечивающий доступ атрибутов к столбцам).

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