Структурированные массивы: приводят ли операции над представлениями к разбросанным массивам? - PullRequest
1 голос
/ 17 апреля 2019

У меня есть простой структурный массив 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.

Ответы [ 2 ]

2 голосов
/ 17 апреля 2019

Мультиполевое индексирование находится в состоянии постоянного изменения. С 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 и 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
0 голосов
/ 17 апреля 2019

Для только сцепления вы можете просто сделать:

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, он все равно не должен работать

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