Структурированные массивы: приводят ли операции с представлениями к разбросанным массивам?

#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 подумали об этой проблеме и представили хороший способ ее решения…