PYTHON/NUMPY: обработка структурированных массивов по сравнению с обычными numpy-массивами Python2.7

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

Итак, здесь я хочу представить "нормальную" версию массива numpy (и что мне нужно с ней делать) и новую версию "структурированного" массива. Мои (самые большие) наборы данных содержат около 200e6 объектов / строк с 40-50 свойствами / столбцами. Все они имеют один и тот же тип данных, за исключением нескольких специальных столбцов: "haloid", "hostid", "type". Это идентификационные номера или флаги, и я должен хранить их вместе с остальными данными, потому что я должен идентифицировать свои объекты с ними.

имя набора данных:

data_array: ndarray shape: (42648, 10)

тип данных:

dt = [('haloid', '<u8'), ('hostid', '<u8'), ('type', 'i1'), 
('mstar', '<f8'), ('x_pos', '<f8'), ('y_pos', '<f8'), 
('z_pos', '<f8'), ('x_vel', '<f8'), ('y_vel', '<f8'), ('z_vel', '<f8')]

Чтение данных из формата.hdf5-файла в массив

Большая часть данных хранится в hdf5-файлах (2000 из них соответствуют одному снимку, который я должен обработать сразу), который должен быть считан в один массив

import numpy as np
import h5py as hdf5

mydict={'name0': 'haloid', 'name1': 'hostid', ...} #dictionary of column names
nr_rows     = 200000                               # approximated
nr_files    = 100                                  # up to 2200
nr_entries  = 10                                   # up to 50   
size        = 0
size_before = 0
new_size    = 0

# normal array:
data_array=np.zeros((nr_rows, nr_entries), dtype=np.float64)
# structured array:
data_array=np.zeros((nr_rows,), dtype=dt)

i=0
while i<nr_files:
    size_before=new_size

    f = hdf5.File(path, "r")
    size=f[mydict['name0']].size

    new_size+=size                

    a=0
    while a<nr_entries:
        name=mydict['name'+str(a)]
        # normal array: 
        data_array[size_before:new_size, a] = f[name] 
        # structured array:
        data_array[name][size_before:new_size] = f[name]                 
        a+=1                
    i+=1

РЕДАКТИРОВАТЬ: Я редактирую код выше, потому что hpaulj, к счастью, комментировал следующее:

Первая точка путаницы. Вы показываете определение DT с именами, такими как dt = [('haloid', '<u8'), ('hostid', '<u8'), ('type', 'i1'),.... Но загрузка h5 - это data_array['name'+str(a)][size_before:new_size] = f['name'+str(a)] Другими словами, в файле есть наборы данных с именами, такими как name0, name1, и вы загружают их в массив с полями с одинаковыми именами.

Это была ошибка копирования / вставки "я-упрощенный код", и я исправил ее!

Вопрос 1: это правильный путь для заполнения структурированного массива?

data_array[name][size_before:new_size] = f[name]

Вопрос 2: Как обратиться к столбцу в структурированном массиве?

data_array[name] #--> column with a certain name

Вопрос 3: Как адресовать всю строку в структурированном массиве?

data_array[0] #--> first row

Вопрос 4: Как обратиться к 3 строкам и всем столбцам?

# normal array:
print data_array[0:3,:]
[[  1.21080866e+10   1.21080866e+10   0.00000000e+00   5.69363234e+08
    1.28992369e+03   1.28894614e+03   1.32171442e+03  -1.08210000e+02
    4.92900000e+02   6.50400000e+01]
 [  1.21080711e+10   1.21080711e+10   0.00000000e+00   4.76329837e+06
    1.29058079e+03   1.28741361e+03   1.32358059e+03  -4.23130000e+02
    5.08720000e+02  -6.74800000e+01]
 [  1.21080700e+10   1.21080700e+10   0.00000000e+00   2.22978043e+10
    1.28750287e+03   1.28864306e+03   1.32270418e+03  -6.13760000e+02
    2.19530000e+02  -2.28980000e+02]]

# structured array:    
print data_array[0:3]
#it returns a lot of data ...
[[ (12108086595L, 12108086595L, 0, 105676938.02998888, 463686295.4907876,.7144191943337, -108.21, 492.9, 65.04)
  (12108071103L, 12108071103L, 0, 0.0, ... more data ...
  ... 228.02) ... more data ...
  (8394715323L, 8394715323L, 2, 0.0, 823505.2374262045, 0798, 812.0612163877823, -541.61, 544.44, 421.08)]]

Вопрос 5: почему data_array[0:3] не вернуть только первые 3 строки с 10 столбцами?

Вопрос 6: Как обратиться к первым двум элементам в первом столбце?

# normal array:
print data_array[0:1,0]
[  1.21080866e+10   1.21080711e+10]
# structured array:  
print data_array['haloid']][0][0:1]  
[12108086595 12108071103]

ХОРОШО! Я понял!

Вопрос 7: Как обратиться к трем конкретным столбцам по имени, и они первые 3 строки в этих столбцах?

# normal array: 
print data_array[0:3, [0,2,1]]
[[  1.21080866e+10   0.00000000e+00   1.21080866e+10]
 [  1.21080711e+10   0.00000000e+00   1.21080711e+10]
 [  1.21080700e+10   0.00000000e+00   1.21080700e+10]]

# structured array:  
print data_array[['haloid','type','hostid']][0][0:3]  
[(12108086595L, 0, 12108086595L) (12108071103L, 0, 12108071103L)
 (12108069992L, 0, 12108069992L)]

ОК, последний пример, кажется, работает!!!

Вопрос 8: В чем разница между:

(А) data_array['haloid'][0][0:3] и (б) data_array['haloid'][0:3]

где (a) возвращает действительно первые три галоида и (b) возвращает много галоидов (10x3).

[[12108086595 12108071103 12108069992 12108076356 12108075899 12108066340
   9248632230 12108066342 10878169355 10077026070]
 [ 6093565531 10077025463  8046772253  7871669276  5558161476  5558161473
  12108068704 12108068708 12108077435 12108066338]
 [ 8739142199 12108069995 12108069994 12108076355 12108092590 12108066312
  12108075900  9248643751  6630111058 12108074389]]

Вопрос 9: что такое data_array['haloid'][0:3] на самом деле возвращается?

Вопрос 10: Как замаскировать структурированный массив с np.where()

# NOTE: col0,1,2 are some integer values of the column I want to address 
# col_name0,1,2 are corresponding names e.g. mstar, type, haloid

# normal array
mask = np.where(data[:,col2] > data[:,col1])
data[mask[:][0]]

mask = np.where(data[:,col2]==2)
data[:,col0][[mask[:][0]]]=data[:,col2][[mask[:][0]]]

#structured array
mask = np.where(data['x_pos'][0] > data['y_pos'][0]])
data[mask[:][0]]

mask = np.where(data[:,col2]==2)
data['haloid'][:,col0][[mask[:][0]]]=data['hostid'][:,col1][[mask[:][0]]]

Кажется, это работает, но я не уверен!

Вопрос 11: Могу ли я использовать np.resize() лайк: data_array = np.resize(data_array,(new_size, nr_entries)) изменить размер / изменить мой массив?

Вопрос 12: Как отсортировать структурированный массив?

# normal array: 
data_sorted = data[np.argsort(data[:,col2])]
# structured array: 
data_sorted = data[np.argsort(data['mstar'][:,col3])]

Спасибо, я ценю любую помощь или совет!

3 ответа

Решение

Первая точка путаницы. Вы показываете dt определение с именами, такими как dt = [('haloid', '<u8'), ('hostid', '<u8'), ('type', 'i1'),..., Но h5 нагрузка

data_array['name'+str(a)][size_before:new_size] = f['name'+str(a)] 

Другими словами, файл имеет наборы данных с такими именами, как name0, name1и вы загружаете их в массив с полями с одинаковыми именами.

Вы можете перебирать поля массива, определенные с помощью dt с помощью

for name in dt.names:
    data[name] = ...

например

In [20]: dt = [('haloid', '<u8'), ('hostid', '<u8'), ('type', 'i1'), 
    ...: ('mstar', '<f8'), ('x_pos', '<f8'), ('y_pos', '<f8'), 
    ...: ('z_pos', '<f8'), ('x_vel', '<f8'), ('y_vel', '<f8'), ('z_vel', '<f8')]
In [21]: arr = np.zeros((3,), dtype=dt)
In [22]: arr
Out[22]: 
array([(0, 0, 0,  0.,  0.,  0.,  0.,  0.,  0.,  0.),
       (0, 0, 0,  0.,  0.,  0.,  0.,  0.,  0.,  0.),
       (0, 0, 0,  0.,  0.,  0.,  0.,  0.,  0.,  0.)], 
      dtype=[('haloid', '<u8'), ('hostid', '<u8'), ('type', 'i1'), ('mstar', '<f8'), ('x_pos', '<f8'), ('y_pos', '<f8'), ('z_pos', '<f8'), ('x_vel', '<f8'), ('y_vel', '<f8'), ('z_vel', '<f8')])
In [23]: for name in arr.dtype.names:
    ...:     print(name)
    ...:     arr[name] = 1
    ...:     
haloid
hostid
 ....
In [24]: arr
Out[24]: 
array([(1, 1, 1,  1.,  1.,  1.,  1.,  1.,  1.,  1.),
       (1, 1, 1,  1.,  1.,  1.,  1.,  1.,  1.,  1.),
       (1, 1, 1,  1.,  1.,  1.,  1.,  1.,  1.,  1.)], 
      dtype=[('haloid', '<u8'), ('hostid', '<u8'), ('type', 'i1'), ('mstar', '<f8'), ('x_pos', '<f8'), ('y_pos', '<f8'), ('z_pos', '<f8'), ('x_vel', '<f8'), ('y_vel', '<f8'), ('z_vel', '<f8')])
In [25]: arr[0]     # get one record
Out[25]: (1, 1, 1,  1.,  1.,  1.,  1.,  1.,  1.,  1.)
In [26]: arr[0]['hostid']     # get one field, one record
In [27]: arr['hostid']       # get all values of a field
Out[27]: array([1, 1, 1], dtype=uint64)
In [28]: arr['hostid'][:2]    # subset of records
Out[28]: array([1, 1], dtype=uint64)

Поэтому заполнение структурированного массива по имени поля должно работать нормально:

arr[name][n1:n2] = file[dataset_name]

Отпечатки как это:

структурированный массив:
print data_array [['haloid', 'type', 'hostid']] [0] [0: 3]
[(12108086595L, 0, 12108086595L) (12108071103L, 0, 12108071103L) (12108069992L, 0, 12108069992L)]

а также

[[ (12108086595L, 12108086595L, 0,

смотреть на меня как структурированный data_array на самом деле 2D, созданный с чем-то вроде (см. вопрос 8)

data_array = np.zeros((10, nr_rows), dtype=dt)

Это единственный способ, которым [0][0:3] индексирование будет работать,

Для 2d массива:

mask = np.where(data[:,col2] > data[:,col1])

сравнивает 2 столбца. Если вы сомневаетесь, сначала смотрите как логическое значение data[:,col2] > data[:,col1], where просто возвращает индексы, где этот логический массив равен True.

Простой пример замаскированной индексации:

In [29]: x = np.array((np.arange(6), np.arange(6)[::-1])).T
In [33]: mask = x[:,0]>x[:,1]
In [34]: mask
Out[34]: array([False, False, False,  True,  True,  True], dtype=bool)
In [35]: idx = np.where(mask)
In [36]: idx
Out[36]: (array([3, 4, 5], dtype=int32),)
In [37]: x[mask,:]
Out[37]: 
array([[3, 2],
       [4, 1],
       [5, 0]])
In [38]: x[idx,:]
Out[38]: 
array([[[3, 2],
        [4, 1],
        [5, 0]]])

В этом структурированном примере data['x_pos'] выбирает поле. [0] требуется выбрать 1-ую строку этого 2-го массива (размер 10). Остальное сравнение и где должно работать как с 2d массивом.

mask = np.where(data['x_pos'][0] > data['y_pos'][0]])

mask[:][0] на where Кортеж, вероятно, не нужен. mask это кортеж, [:] делает копию и [0] выбирает 1-й элемент, который является массивом. Иногда arr[idx[0],:] может понадобиться вместо arr[idx,:], но не делайте этого регулярно.

Мой первый комментарий предложил отдельные массивы

 dt1 = [('haloid', '<u8'), ('hostid', '<u8'), ('type', 'i1')]
 data_id = np.zeros((n,), dtype=dt1)

 data = np.zeros((n,m), dtype=float)    # m float columns

Или даже

 haloid = np.zeros((n,), '<u8')
 hostid = np.zeros((n,), '<u8')
 type = np.zeros((n,), 'i1')

С этими массивами, data_array['hostid'][0], data_id['hostid'] а также hostid должны все возвращать один и тот же массив 1d и быть одинаково пригодными для mask выражения.

Иногда удобно хранить идентификаторы и данные в одной структуре. Это особенно верно, если писать / читать csv форматировать файлы. Но для маскированного выбора это не очень помогает. И для вычислений данных через поля данных это может быть боль.

Я мог бы также предложить составной dtype, один с

dt2 = [('haloid', '<u8'), ('hostid', '<u8'), ('type', 'i1'), ('data', 'f8', (m,))]

In [41]: np.zeros((4,), dtype=dt2)
Out[41]: 
array([(0, 0, 0, [ 0.,  0.,  0.]), (0, 0, 0, [ 0.,  0.,  0.]),
       (0, 0, 0, [ 0.,  0.,  0.]), (0, 0, 0, [ 0.,  0.,  0.])], 
      dtype=[('haloid', '<u8'), ('hostid', '<u8'), ('type', 'i1'), ('data', '<f8', (3,))])
In [42]: _['data']
Out[42]: 
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])

Лучше ли получить доступ к данным с плавающей точкой по номеру столбца или по имени, например "x_coor"? Вам нужно выполнять вычисления с несколькими плавающими столбцами одновременно, или вы всегда будете получать к ним доступ по отдельности?

ОТВЕТ НА ВОПРОС 11:

Вопрос 11: Могу ли я по-прежнему использовать np.resize(), например: data_array = np.resize(data_array,(new_size, nr_entries)), чтобы изменить размер / изменить форму моего массива?

Если я изменяю свой массив таким образом, я создаю для каждого поля в dt Еще 10 столбцов. Итак, я получаю "странный" результат Вопроса 8b: структура (10x3) галоидов

Правильный способ урезать мой массив, потому что я хочу сохранить только заполненную часть if (я спроектировал массив так, чтобы он был достаточно большим, чтобы содержать различное количество блоков данных, которые я читаю впоследствии...), чтобы:

data_array = data_array[:newsize]

print np.info(data_array)

class:  ndarray
shape:  (42648,)
strides:  (73,)
type: [('haloid', '<u8'), ('hostid', '<u8'), ('orphan', 'i1'), 
('mstar', '<f8'), ('x_pos', '<f8'), ('y_pos', '<f8'), 
('z_pos', '<f8'), ('x_vel', '<f8'), ('y_vel', '<f8'), ('z_vel', '<f8')]

Из вашего описания я думаю, что наивный способ - читать только полезные данные в массивы с разными именами (возможно, одного типа?) Если вы хотите, чтобы все данные считывались в одном массиве, возможно, Pandas - ваш выбор: http://pandas.pydata.org/ http://pandas.pydata.org/pandas-docs/stable/ Но я еще не пробовал. Получайте удовольствие, чтобы попробовать.

Другие вопросы по тегам