Конвертируйте фрагмент структурированного массива в обычный массив NumPy в NumPy 1.14 - PullRequest
0 голосов
/ 25 апреля 2018

Примечание 1: Ни один из ответов, заданных на этот вопрос , не работает в моем случае.

Примечание 2: Решение должно работать в NumPy 1.14.

Предположим, что Iиметь следующий структурированный массив:

arr = np.array([(105.0, 34.0, 145.0, 217.0)], dtype=[('a', 'f4'), ('b', 'f4'), ('c', 'f4'), ('d', 'f4')]).

Теперь я разбиваюсь на структурированный тип данных следующим образом:

arr2 = arr[['a', 'b']]

И теперь я пытаюсь преобразовать этот фрагмент в обычный массив:

out = arr2[0].view((np.float32, 2))

, что приводит к

ValueError: Changing the dtype of a 0d array is only supported if the itemsize is unchanged

Что яхотелось бы получить просто обычный массив, например так:

[105.0, 34.0]

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

Я знаю, что это решение работает:

out = np.asarray(list(arr2[0]))

, но я подумалдолжно быть более эффективное решение, чем копирование данных, которые уже находятся в массиве NumPy, в список, а затем обратно в массив.Я предполагаю, что есть способ остаться в NumPy, возможно, вообще не копировать какие-либо данные, я просто не знаю как.

1 Ответ

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

Массив 1d конвертируется с view:

In [270]: arr = np.array([(105.0, 34.0, 145.0, 217.0)], dtype=[('a', 'f4'), ('b','f4'), ('c', 'f4'), ('d', 'f4')])
In [271]: arr
Out[271]: 
array([(105., 34., 145., 217.)],
      dtype=[('a', '<f4'), ('b', '<f4'), ('c', '<f4'), ('d', '<f4')])
In [272]: arr.view('<f4')
Out[272]: array([105.,  34., 145., 217.], dtype=float32)

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

In [273]: arr[0].view('<f4')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-273-70fbab8f61ba> in <module>()
----> 1 arr[0].view('<f4')

ValueError: Changing the dtype of a 0d array is only supported if the itemsize is unchanged

Ранее view часто требуется настройка размеров.Я подозреваю, что с недавними изменениями в обработке структурированных массивов (наиболее очевидно при индексировании нескольких полей одновременно), эта ошибка является результатом, преднамеренно или нет.

Во всем случае массива это изменило 1d, 4массив полей в массив из 1d, 4 элементов, (1,) - (4,).Но при изменении элемента происходит переход от () к (4,).

В прошлом я рекомендовал tolist в качестве надежного способа решения проблемы с viewastype):

In [274]: arr[0].tolist()
Out[274]: (105.0, 34.0, 145.0, 217.0)
In [279]: list(arr[0].tolist())
Out[279]: [105.0, 34.0, 145.0, 217.0]
In [280]: np.array(arr[0].tolist())
Out[280]: array([105.,  34., 145., 217.])

item также является хорошим способом вытащить элемент из его крошечной структуры:

In [281]: arr[0].item()
Out[281]: (105.0, 34.0, 145.0, 217.0)

Результат от tolost и item - это кортеж.

Вы беспокоитесь о скорости.Но вы просто конвертируете один элемент.Одно дело беспокоиться о скорости при использовании tolist в массиве из 1000 элементов, совсем другое - при работе с 1 элементом.

In [283]: timeit arr[0]
131 ns ± 1.31 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [284]: timeit arr[0].tolist()
1.25 µs ± 11.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [285]: timeit arr[0].item()
1.27 µs ± 2.39 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [286]: timeit arr.tolist()
493 ns ± 17.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [287]: timeit arr.view('f4')
1.74 µs ± 18.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Вы можете индексировать элемент способом, который не '• уменьшить размер до 0 (не то, чтобы это сильно помогало со скоростью):

In [288]: arr[[0]].view('f4')
Out[288]: array([105.,  34., 145., 217.], dtype=float32)
In [289]: timeit arr[[0]].view('f4')
6.54 µs ± 15.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [290]: timeit arr[0:1].view('f4')
2.63 µs ± 105 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [298]: timeit arr[0][None].view('f4')
4.28 µs ± 160 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

view все еще требует изменения формы;рассмотрим большой массив:

In [299]: arrs = np.repeat(arr, 10000)
In [301]: arrs.view('f4')
Out[301]: array([105.,  34., 145., ...,  34., 145., 217.], dtype=float32)
In [303]: arrs.shape
Out[303]: (10000,)
In [304]: arrs.view('f4').shape
Out[304]: (40000,)

Представление по-прежнему 1d, где, как мы, вероятно, хотели бы получить (10000,4) -образный двухмерный массив.

Лучшее изменение представления:

In [306]: arrs.view(('f4',4))
Out[306]: 
array([[105.,  34., 145., 217.],
       [105.,  34., 145., 217.],
       [105.,  34., 145., 217.],
       ...,
       [105.,  34., 145., 217.],
       [105.,  34., 145., 217.],
       [105.,  34., 145., 217.]], dtype=float32)
In [307]: _.shape
Out[307]: (10000, 4)

Это работает с массивом из 1 элемента, 1d или 0d:

In [308]: arr.view(('f4',4))
Out[308]: array([[105.,  34., 145., 217.]], dtype=float32)
In [309]: _.shape
Out[309]: (1, 4)
In [310]: arr[0].view(('f4',4))
Out[310]: array([105.,  34., 145., 217.], dtype=float32)
In [311]: _.shape
Out[311]: (4,)

Это было предложено в одном из ответов в вашей ссылке: https://stackoverflow.com/a/10171321/901925

Вопреки вашему комментарию, он работает для меня:

In [312]: arr[0].view((np.float32, len(arr.dtype.names)))
Out[312]: array([105.,  34., 145., 217.], dtype=float32)
In [313]: np.__version__
Out[313]: '1.14.0'

С правкой:

In [84]: arr = np.array([(105.0, 34.0, 145.0, 217.0)], dtype=[('a', 'f4'), ('b','f4'), ('c', 'f4'), ('d', 'f4')])
In [85]: arr2 = arr[['a', 'b']]
In [86]: arr2
Out[86]: 
array([(105., 34.)],
      dtype={'names':['a','b'], 'formats':['<f4','<f4'], 'offsets':[0,4], 'itemsize':16})

In [87]: arr2.view(('f4',2))
...
ValueError: Changing the dtype to a subarray type is only supported if the total itemsize is unchanged

Обратите внимание, что arr2 dtype включает offsetsзначение.В последней версии NumPy изменился выбор нескольких полей.Теперь это истинное представление, сохраняющее исходные данные - все это, а не только выбранные поля.Размер элементов не изменяется:

In [93]: arr.itemsize
Out[93]: 16
In [94]: arr2.itemsize
Out[94]: 16

arr.view(('f4',4) и arr2.view(('f4',4)) производят одно и то же.

Таким образом, вы не можете view (изменить dtype) частичный набор полей,Вы должны сначала взять view всего массива, а затем выбрать строки / столбцы или работать с tolist.

Я использую 1.14.0.В примечаниях к выпуску 1.14.1 говорится:

Изменение в 1.14.0 того, что многополевая индексация структурированных массивов возвращает представление вместо копии, было возвращено, но остается на пути к NumPy 1.15.Затронутые пользователи должны прочитать раздел «Основы / структурированные массивы / доступ к нескольким полям» в руководстве пользователя Numpy 1.14.1, чтобы узнать, как управлять этим переходом.

https://docs.scipy.org/doc/numpy-1.14.2/user/basics.rec.html#accessing-multiple-fields

Thisнаходится в стадии разработки.В этом документе упоминается функция repack_fields, но ее пока нет.

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