#python #numpy #structured-array
#python #numpy #structured-array
Вопрос:
У меня есть numpy структурированный массив a
и создать представление b
на нем:
import numpy as np
a = np.zeros(3, dtype={'names':['A','B','C'], 'formats':['int','int','float']})
b = a[['A', 'C']]
descr
Компонент типа данных b
указывает, что данные хранятся каким-то образом «разбросанными».
>>> b.dtype.descr
[('A', '<i4'), ('', '|V4'), ('C', '<f8')]
(После прочтения документации я считаю, что компонент ('', '|V4')
указывает на «пробел» в данных, так как b
это просто представление a
. )
Если это меня беспокоит, я могу скопировать данные:
import numpy.lib.recfunctions as rf
c = rf.repack_fields(b)
и
>>> c.dtype.descr
[('A', '<i4'), ('C', '<f8')]
по желанию.
Этот шаг требует от меня копирования данных. Теперь иногда я хотел бы применить операцию к представлению. Часто эти операции в любом случае возвращают копию массива. Например,
d = np.concatenate((b,b))
возвращает копию данных в b
и a
. Тем не менее,
>>> d.dtype.descr
[('A', '<i4'), ('', '|V4'), ('C', '<f8')]
указывает, что данные хранятся неэффективно.
Итак, есть ли способ работать с представлениями без получения «разбросанных» результатов? Всегда ли мне нужно было создавать копию заранее? Или нет проблемы с эффективностью, а только странный способ descr
описания типа данных? (Если да, то как я могу этого избежать?)
Этот вопрос становится особенно актуальным, если я хочу пренебречь промежуточными шагами:
d = np.concatenate((a[['A', 'C']], a[['A', 'C']]))
Я работаю с numpy 1.16 и python 3.7.
Ответ №1:
Многопольное индексирование уже некоторое время находится в постоянном состоянии. 1.16
Похоже, что они остановились на этой «смещенной» форме «представлений», требующей явной переупаковки, если вы хотите получить «чистую» копию.
In [231]: np.__version__
Out[231]: '1.16.1'
In [232]: a.dtype
Out[232]: dtype([('A', '<i8'), ('B', '<i8'), ('C', '<f8')])
In [233]: a[['A','C']].dtype
Out[233]: dtype({'names':['A','C'], 'formats':['<i8','<f8'], 'offsets':[0,16], 'itemsize':24})
В этом представлении значения для ‘B’ все еще присутствуют (со смещением 8). Представьте, что буфер данных имеет:
[a0, b0, c0, a1, b1, c1, a2, b2, c2, ....]
[233] ‘view’ просматривает тот же буфер данных, но дает нам доступ только к полям A
and C
. repack_fields
создает новый буфер данных с:
[a0, c0, a1, c1, ....]
Если a
бы это был обычный (n,3)
массив, a[:, [0,2]]
это была бы копия. Мы не могли пропустить a[:,1]
и все еще иметь представление.
In [234]: np.concatenate((a[['A','C']],a[['A','C']]))
Out[234]:
array([(0, 0.), (1, 1.), (2, 2.), (0, 0.), (1, 1.), (2, 2.)],
dtype={'names':['A','C'], 'formats':['<i8','<f8'], 'offsets':[0,16], 'itemsize':24})
Играя с view
, я обнаружил, что поле со смещением 8 (поле ‘B’ в a
) все еще существует, но неинициализировано (как в np.empty
массиве).
Различные способы отображения этого «разбросанного» dtype:
In [238]: a1.dtype
Out[238]: dtype({'names':['A','C'], 'formats':['<i8','<f8'], 'offsets':[0,16], 'itemsize':24})
In [239]: a1.dtype.descr
Out[239]: [('A', '<i8'), ('', '|V8'), ('C', '<f8')]
In [241]: a1.dtype.fields
Out[241]: mappingproxy({'A': (dtype('int64'), 0), 'C': (dtype('float64'), 16)})
Я также могу изменить порядок полей:
In [248]: a[['B','C','A']].dtype
Out[248]: dtype({'names':['B','C','A'], 'formats':['<i8','<f8','<i8'], 'offsets':[8,16,0], 'itemsize':24})
In [249]: a[['B','C','A']].dtype.descr
...
ValueError: dtype.descr is not defined for types with overlapping or out-of-order fields
Комментарии:
1. Спасибо, вы хорошо описываете то, что я небрежно назвал «разбросанными данными», и вы не показываете, что проблема действительно существует (что было одним из моих вопросов). У вас также есть идея, как я мог бы избежать такого «разбросанного типа данных» (без повторного копирования всего)? Если для этого нет встроенного способа, это тоже ответ. Тогда меня интересовал бы самый элегантный обходной путь.
2. Весь смысл изменений 1.16 заключается в том, что многопольное индексирование создает представление, а не копию. Функция repack предоставляется, если вы хотите использовать более старое поведение копирования. Копирование поля, которое вы сделали в другом вопросе, использует новое поведение представления.
Ответ №2:
Только для объединения вы можете просто сделать:
a = np.array([(1,2,3),(4,5,6)], 'f,f,f')
view = a[['f0','f2']]
b = np.empty(4, 'f,f')
b[:2] = view
b[2:] = view
print(b)
вывод:
array([(1., 3.), (4., 6.), (1., 3.), (4., 6.)],
dtype=[('f0', '<f4'), ('f1', '<f4')])
РЕДАКТИРОВАТЬ: забудьте о том, о чем я сказал np.add
, это все равно не должно работать
Комментарии:
1. Спасибо за ваш ответ. Однако мне действительно был бы интересен более общий ответ. Я знаю, что могу создавать обходные пути. Я просто подумал, что разработчики numpy подумали об этой проблеме и представили хороший способ ее решения…