python h5py: могу ли я хранить набор данных, у которых разные столбцы имеют разные типы?

Предположим, у меня есть таблица, которая имеет много столбцов, только несколько столбцов имеют тип с плавающей запятой, другие - маленькие целые числа, например:

col1, col2, col3, col4
1.31   1      2     3
2.33   3      5     4
...

Как я могу хранить это эффективно, если я использую np.float32 для этого набора данных хранилище тратится впустую, потому что другие столбцы имеют только небольшое целое число, им не нужно так много места. Если я использую np.int16, столбец с плавающей точкой не является точным, что также, что я хотел. Поэтому как мне справиться с такой ситуацией?

Предположим, у меня также есть строковый столбец, который делает меня более запутанным, как мне хранить данные?

col1, col2, col3, col4, col5
1.31   1      2     3    "a"
2.33   3      5     4    "b"
...

Редактировать:

Чтобы упростить ситуацию, предположим, что в столбце строк есть только строки фиксированной длины, например, длина 3.

1 ответ

Решение

Я собираюсь продемонстрировать подход структурированного массива:

Я предполагаю, что вы начинаете с CSV-файла "таблица". Если нет, то это все же самый простой способ превратить ваш образец в массив:

In [40]: txt = '''col1, col2, col3, col4, col5
    ...: 1.31   1      2     3    "a"
    ...: 2.33   3      5     4    "b"
    ...: '''


In [42]: data = np.genfromtxt(txt.splitlines(), names=True, dtype=None, encoding=None)

In [43]: data
Out[43]: 
array([(1.31, 1, 2, 3, '"a"'), (2.33, 3, 5, 4, '"b"')],
      dtype=[('col1', '<f8'), ('col2', '<i8'), ('col3', '<i8'), ('col4', '<i8'), ('col5', '<U3')])

С этими параметрами, genfromtxt заботится о создании структурированного массива. Обратите внимание, что это 1d массив с 5 полями. Поля dtype определяются из данных.

In [44]: import h5py
...

In [46]: f = h5py.File('struct.h5', 'w')

In [48]: ds = f.create_dataset('data',data=data)
...
TypeError: No conversion path for dtype: dtype('<U3')

Но h5py возникают проблемы при сохранении строк в Юникоде (по умолчанию для py3). Может быть несколько способов обойти это, но здесь будет проще преобразовать строку типа d в ​​строки байтов. Кроме того, это будет более компактно.

Чтобы преобразовать это, я сделаю новый dtypeи использовать astype, В качестве альтернативы я мог бы указать dtypes в genfromtxt вызов.

In [49]: data.dtype
Out[49]: dtype([('col1', '<f8'), ('col2', '<i8'), ('col3', '<i8'), ('col4', '<i8'), ('col5', '<U3')])

In [50]: data.dtype.descr
Out[50]: 
[('col1', '<f8'),
 ('col2', '<i8'),
 ('col3', '<i8'),
 ('col4', '<i8'),
 ('col5', '<U3')]

In [51]: dt1 = data.dtype.descr

In [52]: dt1[-1] = ('col5', 'S3')

In [53]: data.astype(dt1)
Out[53]: 
array([(1.31, 1, 2, 3, b'"a"'), (2.33, 3, 5, 4, b'"b"')],
      dtype=[('col1', '<f8'), ('col2', '<i8'), ('col3', '<i8'), ('col4', '<i8'), ('col5', 'S3')])

Теперь он сохраняет массив без проблем:

In [54]: data1 = data.astype(dt1)

In [55]: data1
Out[55]: 
array([(1.31, 1, 2, 3, b'"a"'), (2.33, 3, 5, 4, b'"b"')],
      dtype=[('col1', '<f8'), ('col2', '<i8'), ('col3', '<i8'), ('col4', '<i8'), ('col5', 'S3')])

In [56]: ds = f.create_dataset('data',data=data1)

In [57]: ds
Out[57]: <HDF5 dataset "data": shape (2,), type "|V35">

In [58]: ds[:]
Out[58]: 
array([(1.31, 1, 2, 3, b'"a"'), (2.33, 3, 5, 4, b'"b"')],
      dtype=[('col1', '<f8'), ('col2', '<i8'), ('col3', '<i8'), ('col4', '<i8'), ('col5', 'S3')])

Я мог бы внести дополнительные изменения, сократив одно или несколько полей int:

In [60]: dt1[1] = ('col2','i2')    
In [61]: dt1[2] = ('col3','i2')

In [62]: dt1
Out[62]: 
[('col1', '<f8'),
 ('col2', 'i2'),
 ('col3', 'i2'),
 ('col4', '<i8'),
 ('col5', 'S3')]

In [63]: data1 = data.astype(dt1)

In [64]: data1
Out[64]: 
array([(1.31, 1, 2, 3, b'"a"'), (2.33, 3, 5, 4, b'"b"')],
      dtype=[('col1', '<f8'), ('col2', '<i2'), ('col3', '<i2'), ('col4', '<i8'), ('col5', 'S3')])

In [65]: ds1 = f.create_dataset('data1',data=data1)

ds1 имеет более компактное хранилище, "V23" против "V35"

In [67]: ds1
Out[67]: <HDF5 dataset "data1": shape (2,), type "|V23">

In [68]: ds1[:]
Out[68]: 
array([(1.31, 1, 2, 3, b'"a"'), (2.33, 3, 5, 4, b'"b"')],
      dtype=[('col1', '<f8'), ('col2', '<i2'), ('col3', '<i2'), ('col4', '<i8'), ('col5', 'S3')])
Другие вопросы по тегам