合并hdf5文件

31

我有许多hdf5文件,每个文件都有一个单独的数据集。这些数据集太大了,无法保存在RAM中。 我想将这些文件合并成一个文件,其中包含所有单独的数据集(即不是将数据集连接成一个数据集)。

一种方法是创建一个hdf5文件,然后逐个复制数据集。 这将是缓冲复制,因此速度较慢且复杂。

有没有更简单的方法?看起来应该有,因为实质上只是创建一个容器文件。

我正在使用python/h5py。


1
看起来这个问题已经有答案了:https://dev59.com/dW435IYBdhLWcg3wjw3S - Matt Pavelle
2
据我所理解,@MattPavelle,这与我的需求不同。我不想将数据集连接成一个单一的数据集,而是要将它们作为单独的数据集保存在一个文件中。 - Bitwise
1
明白了,感谢您的澄清和编辑。不好意思再问一句——我已经有几年没用HDF5了,但我猜h5merge不能解决这个问题? - Matt Pavelle
@MattPavelle 不太确定,现在正在看。h5merge似乎不是官方的hdf5工具之一,而且其文档似乎有些欠缺。我更多地是在寻找一个python/h5py解决方案,但我也会进一步探索可用的hdf5 unix工具。谢谢。 - Bitwise
是的,它不是官方的hdf5工具 - 而且它绝对不是Pythonic的 :) 但它可能是你最好的选择。 - Matt Pavelle
6个回答

36

实际上,这是HDF5的用例之一。如果您只想从单个文件中访问所有数据集,并且不关心它们在磁盘上的实际存储方式,您可以使用外部链接。来自HDF5网站

外部链接允许组将另一个HDF5文件中的对象包含在内,并使库能够访问这些对象,就好像它们在当前文件中一样。通过这种方式,组似乎直接包含了数据集、命名数据类型,甚至是实际上位于不同文件中的组。该功能通过一系列函数实现,这些函数创建和管理链接,定义和检索到外部对象的路径,并解释链接名称:

以下是在h5py中执行此操作的方法

myfile = h5py.File('foo.hdf5','a')
myfile['ext link'] = h5py.ExternalLink("otherfile.hdf5", "/path/to/resource")
注意:打开myfile文件时,如果它是现有文件,则应使用'a'打开。如果您使用'w'打开它,则会擦除其内容。
这比将所有数据集复制到新文件中要快得多。我不知道访问otherfile.hdf5有多快,但操作所有数据集将是透明的 - 也就是说,h5py将看到所有数据集都存在于foo.hdf5中。

谢谢,这是个不错的技巧。但在我的情况下,我更喜欢它们真正被包含在一个文件中。但如果复制证明太慢,我可能会使用这种方法。 - Bitwise
3
应该选择这个作为问题的答案。 - ivotron
如果您要执行此操作并且有大量链接,请确保在使用C中的H5Pset_libver_bounds()或在h5py中创建/打开新文件时使用libver='latest'。这将使用最新的文件格式,对于存储大量链接来说更加高效。 - Dana Robinson
2
我刚刚尝试了你的建议,它把一个大小为98.5GB的数据集"myfile"变成了960字节;现在我必须重新创建它。没有任何警告或提示——98.5GB消失了! - Jeshua Lacock
@JeshuaLacock 当你使用 'w' 打开文件时会出现这种情况。我认为正确答案应该是 myfile = h5py.File('foo.hdf5', 'a') - Yamaneko
当您尝试使用scp传输链接到其他h5文件的h5文件时会发生什么? - Alex Lenail

17

一个解决方案是使用 h5py 接口来调用 HDF5 API 的低级别 H5Ocopy 函数,特别是使用 h5py.h5o.copy 函数:

In [1]: import h5py as h5

In [2]: hf1 = h5.File("f1.h5")

In [3]: hf2 = h5.File("f2.h5")

In [4]: hf1.create_dataset("val", data=35)
Out[4]: <HDF5 dataset "val": shape (), type "<i8">

In [5]: hf1.create_group("g1")
Out[5]: <HDF5 group "/g1" (0 members)>

In [6]: hf1.get("g1").create_dataset("val2", data="Thing")
Out[6]: <HDF5 dataset "val2": shape (), type "|O8">

In [7]: hf1.flush()

In [8]: h5.h5o.copy(hf1.id, "g1", hf2.id, "newg1")

In [9]: h5.h5o.copy(hf1.id, "val", hf2.id, "newval")

In [10]: hf2.values()
Out[10]: [<HDF5 group "/newg1" (1 members)>, <HDF5 dataset "newval": shape (), type "<i8">]

In [11]: hf2.get("newval").value
Out[11]: 35

In [12]: hf2.get("newg1").values()
Out[12]: [<HDF5 dataset "val2": shape (), type "|O8">]

In [13]: hf2.get("newg1").get("val2").value
Out[13]: 'Thing'

以上内容是在Debian Wheezy基本安装的前提下,使用h5py版本2.0.1-2+b1和iPython版本0.13.1-2+deb7u1及Python版本2.7.3-4+deb7u1生成的。在执行上述操作之前,文件f1.h5f2.h5不存在。 请注意,根据salotz的建议,对于Python 3,数据集/组名称需要为 bytes(例如 b"val"),而不是 str

[7]命令中的hf1.flush()非常关键,因为低级接口似乎总是从存储在磁盘上的.h5文件版本中读取,而不是从内存中缓存读取。通过使用hf1.get("g1").id提供该组的ID,可以将数据集复制到/从未在File根目录下的组中。

请注意,如果已在目标位置中存在指定名称的对象,则h5py.h5o.copy将失败并引发异常(无覆盖)。


1
这看起来可能晚了几年,但是... <耸肩> 我一定会使用它,希望至少能帮助其他人。 - hBy2Py
1
谢谢!实际上,这个问题每隔一段时间就会得到投票,所以我猜它对很多人仍然有用。 - Bitwise
很好。HDF5是一种非常好的数据格式,但它的高级API远远不够详尽。 - hBy2Py
2
我正在使用h5py 2.7.1和Python 3.6.5,需要将字符串转换为字节,因此请将h5.h5o.copy(hf1.id, "g1", hf2.id, "newg1")替换为h5.h5o.copy(hf1.id, b"g1", hf2.id, b"newg1") - salotz

14
我发现使用官方HDF5工具中的h5copy可以找到非Python解决方案。 h5copy可以将指定的单个数据集从一个HDF5文件复制到另一个现有的HDF5文件中。
如果有人找到基于Python/h5py的解决方案,我会很高兴听到。

2

我通常使用ipythonh5copy工具结合使用,与纯python解决方案相比速度更快。安装h5copy后即可使用。

控制台解决方案 M.W.E.

#PLESE NOTE THIS IS IPYTHON CONSOLE CODE NOT PURE PYTHON

import h5py
#for every dataset Dn.h5 you want to merge to Output.h5 
f = h5py.File('D1.h5','r+') #file to be merged 
h5_keys = f.keys() #get the keys (You can remove the keys you don't use)
f.close() #close the file
for i in h5_keys:
        !h5copy -i 'D1.h5' -o 'Output.h5' -s {i} -d {i}

自动化控制台解决方案

为了完全自动化该过程,假设您正在工作的文件夹中存储了要合并的文件:

import os 
d_names = os.listdir(os.getcwd())
d_struct = {} #Here we will store the database structure
for i in d_names:
   f = h5py.File(i,'r+')
   d_struct[i] = f.keys()
   f.close()

# A) empty all the groups in the new .h5 file 
for i in d_names:
    for j  in d_struct[i]:
        !h5copy -i '{i}' -o 'output.h5' -s {j} -d {j}

为每个添加的.h5文件创建一个新组

如果您想要在输出文件output.h5中保留先前的数据集,您需要首先使用标志-p创建组:

 # B) Create a new group in the output.h5 file for every input.h5 file
 for i in d_names:
        dataset = d_struct[i][0]
        newgroup = '%s/%s' %(i[:-3],dataset)
        !h5copy -i '{i}' -o 'output.h5' -s {dataset} -d {newgroup} -p
        for j  in d_struct[i][1:]:
            newgroup = '%s/%s' %(i[:-3],j) 
            !h5copy -i '{i}' -o 'output.h5' -s {j} -d {newgroup}

2
更新一下,随着HDF5版本1.10的推出,出现了一个新功能叫做"虚拟数据集",在这种情况下可能会有用。
您可以在以下链接中找到简短的教程和一些解释:虚拟数据集
此处提供更完整和详细的功能解释和文档:
虚拟数据集额外文档
并且在h5py中包含虚拟数据集API的合并拉取请求如下:
h5py虚拟数据集PR,但我不知道它是否已经在当前的h5py版本中可用或者将来会更新。

创建虚拟数据集将(虚拟地)连接数据集,这不是原始帖子作者想要做的。 - Dana Robinson

2
使用Python(而不是IPython)和h5copy合并HDF5文件,我们可以基于GM的答案进行构建:
import h5py
import os

d_names = os.listdir(os.getcwd())
d_struct = {} #Here we will store the database structure
for i in d_names:
   f = h5py.File(i,'r+')
   d_struct[i] = f.keys()
   f.close()

for i in d_names:
   for j  in d_struct[i]:
      os.system('h5copy -i %s -o output.h5 -s %s -d %s' % (i, j, j))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接