【问题标题】:Can I use h5py to write strings to an HDF5 file in one line, rather than looping over entries?我可以使用 h5py 在一行中将字符串写入 HDF5 文件,而不是遍历条目吗?
【发布时间】:2021-09-30 15:45:23
【问题描述】:

我需要使用h5py 将字符串列表/数组存储在 HDF5 文件中。这些字符串是可变长度的。按照我在网上找到的示例,我有一个可以工作的脚本。

import h5py
  
h5File=h5py.File('outfile.h5','w')

data=['this','is','a','sentence']

dt = h5py.special_dtype(vlen=str)

dset = h5File.create_dataset('words',(len(data),1),dtype=dt)
for i,word in enumerate(data):
    dset[i] = word

h5File.flush()
h5File.close()

但是,当data 变得非常大时,写入需要很长时间,因为它会遍历每个条目并将其插入到文件中。

我认为我可以在一行中完成所有操作,就像使用整数或浮点数一样。但以下脚本失败。请注意,我添加了一些代码来测试 int 是否有效。

import h5py

h5File=h5py.File('outfile.h5','w')

data_numbers = [0, 1, 2, 3, 4]
data = ['this','is','a','sentence']

dt = h5py.special_dtype(vlen=str)

dset_num = h5File.create_dataset('numbers',(len(data_numbers),1),dtype=int,data=data_numbers)
print("Created the dataset with numbers!\n")

dset_str = h5File.create_dataset('words',(len(data),1),dtype=dt,data=data)
print("Created the dataset with strings!\n")

h5File.flush()
h5File.close()

该脚本给出以下输出。

Created the dataset with numbers!

Traceback (most recent call last):
  File "write_strings_to_HDF5_file.py", line 32, in <module>
    dset_str = h5File.create_dataset('words',(len(data),1),dtype=dt,data=data)
  File "/opt/anaconda3/lib/python3.7/site-packages/h5py/_hl/group.py", line 136, in create_dataset
    dsid = dataset.make_new_dset(self, shape, dtype, data, **kwds)
  File "/opt/anaconda3/lib/python3.7/site-packages/h5py/_hl/dataset.py", line 170, in make_new_dset
    dset_id.write(h5s.ALL, h5s.ALL, data)
  File "h5py/_objects.pyx", line 54, in h5py._objects.with_phil.wrapper
  File "h5py/_objects.pyx", line 55, in h5py._objects.with_phil.wrapper
  File "h5py/h5d.pyx", line 211, in h5py.h5d.DatasetID.write
  File "h5py/h5t.pyx", line 1652, in h5py.h5t.py_create
  File "h5py/h5t.pyx", line 1713, in h5py.h5t.py_create
TypeError: No conversion path for dtype: dtype('<U8')

我已阅读有关 UTF-8 编码的文档,并尝试了上述语法的多种变体,但我似乎遗漏了一些关键点。可能做不到?

感谢任何有建议的人!


如果有人想看到有效示例的减速,这里有一个测试用例。

import h5py

h5File=h5py.File('outfile.h5','w')

sentence=['this','is','a','sentence']
data = []

for i in range(10000):
    data += sentence

print(len(data))

dt = h5py.special_dtype(vlen=str)

dset = h5File.create_dataset('words',(len(data),1),dtype=dt)
for i,word in enumerate(data):
    dset[i] = word

h5File.flush()
h5File.close()

【问题讨论】:

标签: python hdf5 h5py


【解决方案1】:

一次写入 1 行数据是写入 HDF5 文件的最慢方法。当您写入 100 行时您不会注意到性能问题,但是随着行数的增加您会看到它。还有另一个答案讨论了这个问题。请参阅:pytables writes much faster than h5py. Why?(注意:我不建议您使用PyTables。链接的答案显示了h5pyPyTables 的性能)。如您所见,写入大量小块时,写入相同数量的数据需要更长的时间。

为了提高性能,您需要每次写入更多数据。由于您已在列表data 中加载了所有数据,因此您可以一次性完成。对于 10,000 行,它几乎是瞬时的。 cmets 中引用的答案涉及此技术(从列表数据创建np.array()。但是,它适用于小列表(1/行)......所以不完全相同。创建时必须小心数组。您不能使用 NumPy 的默认 Unicode dtype - h5py 不支持它。相反,您需要 dtype='S#'

下面的代码显示将您的字符串列表转换为字符串的np.array()。另外,我强烈建议您使用 Python 的 with/as: 联系人管理器来打开文件。这可以避免文件由于意外退出(由于崩溃或逻辑错误)而意外打开的情况。

代码如下:

import h5py
import numpy as np

sentence=['this','is','a','sentence']
data = []

for i in range(10_000):
    data += sentence
print(len(data))
longest_word=len(max(data, key=len))
print('longest_word=',longest_word)

dt = h5py.special_dtype(vlen=str)

arr = np.array(data,dtype='S'+str(longest_word))
with h5py.File('outfile.h5','w') as h5File:
    dset = h5File.create_dataset('words',data=arr,dtype=dt)
    print(dset.shape, dset.dtype)

【讨论】:

  • 我认为显着的变化是您创建了一个 S8,字节串数组,而 OP 使用 data=data 的尝试是隐式尝试保存 data=np.array(data),从而产生一个“U8”数组。 h5py 不能(至少现在)将 numpy unicode dtype 转换为 vlen 字符串。
  • Ahhh...我从最后的工作示例开始,只是查看了非工作示例中的问题。是的,错误是由于他的字符串列表的“自动魔术”内部转换为不受支持的 Unicode 数据的 numpy 数组。 h5py 的限制来自 HDF5。它不支持宽字符。因此,它适用于所有字符串数据——包括固定长度和可变长度——dtype 必须是"S#" 而不是"&lt;U#"。解决方案并不优雅,但在约束条件下我找到了最好的解决方案。
  • 啊,这太棒了@kcw78!!!!谢谢!!!这很棒!并感谢您清楚地解释为什么原始代码失败。这非常有帮助。
  • 谢谢@kcw78!
猜你喜欢
  • 2015-09-17
  • 2016-10-04
  • 2014-06-20
  • 2022-11-11
  • 1970-01-01
  • 2014-10-07
  • 2012-02-01
  • 2021-08-05
  • 2014-10-28
相关资源
最近更新 更多