如何压缩 hdf5 中的 lists/nested 列表

how to compress lists/nested lists in hdf5

我最近了解到 hdf5 压缩并使用它。在处理巨大的文件时,它比 .npz/npy 有一些优势。 我设法尝试了一个小列表,因为我有时会使用具有如下字符串的列表;

def write():
    test_array = ['a1','a2','a1','a2','a1','a2', 'a1','a2', 'a1','a2','a1','a2','a1','a2', 'a1','a2', 'a1','a2','a1','a2','a1','a2', 'a1','a2']
    

    with  h5py.File('example_file.h5', 'w') as f:
        f.create_dataset('test3', data=repr(test_array), dtype='S', compression='gzip', compression_opts=9) 
        f.close()
    

但是我得到了这个错误:

f.create_dataset('test3', data=repr(test_array), dtype='S', compression='gzip', compression_opts=9)
  File "/usr/local/lib/python3.6/dist-packages/h5py/_hl/group.py", line 136, in create_dataset
    dsid = dataset.make_new_dset(self, shape, dtype, data, **kwds)
  File "/usr/local/lib/python3.6/dist-packages/h5py/_hl/dataset.py", line 118, in make_new_dset
    tid = h5t.py_create(dtype, logical=1)
  File "h5py/h5t.pyx", line 1634, in h5py.h5t.py_create
  File "h5py/h5t.pyx", line 1656, in h5py.h5t.py_create
  File "h5py/h5t.pyx", line 1689, in h5py.h5t.py_create
  File "h5py/h5t.pyx", line 1508, in h5py.h5t._c_string
ValueError: Size must be positive (size must be positive)

在网上搜索了几个小时以找到更好的方法后,我还是找不到。 有没有更好的方法用H5压缩列表?

你很接近。 data= 参数旨在与现有的 NumPy 数组一起使用。当您使用列表时,它会在后台转换为数组。它适用于数字列表。 (请注意,列表和数组是不同的 Python 对象 类。)

您 运行 遇到转换字符串列表的问题。默认情况下,dtype 设置为 NumPy 的 Unicode 类型(在您的情况下为“HDF5 不支持宽字符。如果您尝试存储这种类型的数据,h5py 不会尝试绕过它并“假装”支持它,而是会引发错误。" link 处有关 NumPy 和字符串的完整详细信息:h5py doc: Strings in HDF5

我稍微修改了您的示例以展示如何让它工作。请注意,我明确创建了 NumPy 字符串数组,并声明了 dtype='S2' 以获得所需的字符串 dtype。我添加了一个使用整数列表的示例来展示列表如何处理数字。 但是,NumPy 数组是首选数据对象。

我删除了 f.close() 语句,因为在使用上下文管理器(with / as: 结构)时不需要这样做

此外,请注意压缩级别。与 compression_opts=1 相比,compression_opts=9 的压缩率会(稍微)高一些,但每次访问数据集时都会花费 I/O 的处理时间。我建议从 1.

开始
import h5py
import numpy as np

test_array = np.array(['a1','a2','a1','a2','a1','a2', 'a1','a2', 
                       'a1','a2','a1','a2','a1','a2', 'a1','a2', 
                       'a1','a2','a1','a2','a1','a2', 'a1','a2'], dtype='S2')

data_list = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

with h5py.File('example_file.h5', 'w') as f:
     f.create_dataset('test3', data=test_array, compression='gzip', compression_opts=9) 

     f.create_dataset('test4', data=data_list, compression='gzip', compression_opts=1) 

这是嵌套列表的更通用的答案,其中每个嵌套列表的长度不同。当嵌套列表长度相等时,它也适用于更简单的情况。有 2 种解决方案:一种使用 h5py,一种使用 PyTables。

h5py 示例
h5py 不支持参差不齐的数组,因此您必须根据最长的子字符串创建一个数据集,并将元素添加到“短”子字符串中。 您将在嵌套列表中没有相应值的每个数组位置获得 'None'(或子字符串)。注意 dtype= 条目。这显示了如何在列表中找到最长的字符串(如 slen=##)并使用它来创建 dtype='S##'

import h5py
import numpy as np

test_list = [['a01','a02','a03','a04','a05','a06'], 
             ['a11','a12','a13','a14','a15','a16','a17'], 
             ['a21','a22','a23','a24','a25','a26','a27','a28']]

# arrlen and test_array from answer to SO #10346336 - Option 3:
# Ref:     
slen = max(len(item) for sublist in test_list for item in sublist)
arrlen = max(map(len, test_list))
test_array = np.array([tl+[None]*(arrlen-len(tl)) for tl in test_list], dtype='S'+str(slen))
  
with h5py.File('example_nested.h5', 'w') as f:
     f.create_dataset('test3', data=test_array, compression='gzip')

PyTables 示例
PyTables 支持参差不齐的二维数组作为 VLArrays(可变长度)。这避免了为“短”子字符串添加 'None' 值的复杂性。此外,您不必提前确定数组长度,因为在创建 VLArray 时未定义行数(行在创建后添加)。再次注意 dtype= 条目。这使用与上述相同的方法。

import tables as tb
import numpy as np

test_list = [['a01','a02','a03','a04','a05','a06'], 
             ['a11','a12','a13','a14','a15','a16','a17'], 
             ['a21','a22','a23','a24','a25','a26','a27','a28']]
   
slen = max(len(item) for sublist in test_list for item in sublist)

with tb.File('example_nested_tb.h5', 'w') as h5f:        
    vlarray = h5f.create_vlarray('/','vla_test', tb.StringAtom(slen) ) 
    for slist in test_list:
        arr = np.array(slist,dtype='S'+str(slen))
        vlarray.append(arr)

    print('-->', vlarray.name)
    for row in vlarray:
        print('%s[%d]--> %s' % (vlarray.name, vlarray.nrow, row))