如何合并多个 .h5 文件?
Posted
技术标签:
【中文标题】如何合并多个 .h5 文件?【英文标题】:How can I combine multiple .h5 file? 【发布时间】:2020-01-30 21:02:08 【问题描述】:网上所有的东西都太复杂了。我的数据库很大,我将其部分导出。我现在有三个 .h5 文件,我想将它们组合成一个 .h5 文件以供进一步工作。我该怎么做?
【问题讨论】:
我的建议是使用h5py
库编写一个简单的 Python 代码
【参考方案1】:
至少有 3 种方法可以将单个 HDF5 文件中的数据合并到一个文件中:
-
使用外部链接创建指向其他文件中数据的新文件(需要 pytables/tables 模块)
使用 HDF 组实用程序复制数据:h5copy.exe
使用 Python 复制数据(使用 h5py 或 pytables)
此处提供外部链接示例:https://***.com/a/55399562/10462884 它展示了如何创建链接以及如何取消引用它们。
h5copy 的文档在这里:https://support.hdfgroup.org/HDF5/doc/RM/Tools.html#Tools-Copy
使用 h5py 或 pytables 复制更多。
【讨论】:
我想出了一个方法,如果这是正确的方法,请告诉我:首先我使用 Pandas 读取我的 .h5 文件,然后使用 pandas 的“to_csv()”函数保存它转换成 csv 格式。合并多个 csv 文件比 .h5 文件容易得多,文件大小几乎保持不变。这是其中一种方法吗? 如果它适合您,那么这是另一种选择(特别是如果您只想这样做一次并且可以在您的流程中使用 CSV 文件)。但是,如果我必须经常这样做或需要下游的 HDF5,我会使用上述方法之一来避免创建和组合 csv 文件。【参考方案2】:这些示例展示了如何使用 h5py 在 2 个 HDF5 文件之间复制数据集。有关 PyTables 示例,请参阅我的其他答案。我创建了一些简单的 HDF5 文件来模拟 CSV 类型的数据(所有浮点数,但如果您有混合数据类型,过程是相同的)。根据您的描述,每个文件只有一个数据集。当您有多个数据集时,您可以在 h5py 中使用visititems()
扩展此过程。
注意:创建示例中使用的 HDF5 文件的代码在最后。
所有方法都使用glob()
来查找下面操作中用到的HDF5文件。
方法一:创建外部链接 这会在新的 HDF5 文件中生成 3 个组,每个组都有指向原始数据的外部链接。 这不会复制数据,而是通过 1 个文件中的链接提供对所有文件中数据的访问。
with h5py.File('table_links.h5',mode='w') as h5fw:
link_cnt = 0
for h5name in glob.glob('file*.h5'):
link_cnt += 1
h5fw['link'+str(link_cnt)] = h5py.ExternalLink(h5name,'/')
方法 2a:“按原样”复制数据
(2020 年 5 月 26 日更新:这对所有数据集使用 .copy()
方法。)
这会将原始文件中每个数据集的数据复制到使用原始数据集名称的新文件中。它循环复制所有根级数据集。 这要求每个文件中的数据集具有不同的名称。数据不会合并到一个数据集中。
with h5py.File('table_copy.h5',mode='w') as h5fw:
for h5name in glob.glob('file*.h5'):
h5fr = h5py.File(h5name,'r')
for obj in h5fr.keys():
h5r.copy(obj, h5fw)
方法 2b:“按原样”复制数据
(这是我最初的答案,在我知道 .copy()
方法之前。)
这会将原始文件中每个数据集的数据复制到使用原始数据集名称的新文件中。 这要求每个文件中的数据集具有不同的名称。数据不会合并到一个数据集中。
with h5py.File('table_copy.h5',mode='w') as h5fw:
for h5name in glob.glob('file*.h5'):
h5fr = h5py.File(h5name,'r')
dset1 = list(h5fr.keys())[0]
arr_data = h5fr[dset1][:]
h5fw.create_dataset(dset1,data=arr_data)
方法 3a:将所有数据合并到 1 个固定大小的数据集中 这会将原始文件中每个数据集的数据复制并合并到新文件中的单个数据集中。 在此示例中,对数据集名称没有限制。此外,我最初创建了一个大型数据集并且不调整大小。这假设有足够的行来保存所有合并的数据。应该在生产工作中添加测试。
with h5py.File('table_merge.h5',mode='w') as h5fw:
row1 = 0
for h5name in glob.glob('file*.h5'):
h5fr = h5py.File(h5name,'r')
dset1 = list(h5fr.keys())[0]
arr_data = h5fr[dset1][:]
h5fw.require_dataset('alldata', dtype="f", shape=(50,5), maxshape=(100, 5) )
h5fw['alldata'][row1:row1+arr_data.shape[0],:] = arr_data[:]
row1 += arr_data.shape[0]
方法 3b:将所有数据合并到 1 个可调整大小的数据集 这与上面的方法类似。但是,我创建了一个可调整大小的数据集,并根据读取和添加的数据量进行放大。
with h5py.File('table_merge.h5',mode='w') as h5fw:
row1 = 0
for h5name in glob.glob('file*.h5'):
h5fr = h5py.File(h5name,'r')
dset1 = list(h5fr.keys())[0]
arr_data = h5fr[dset1][:]
dslen = arr_data.shape[0]
cols = arr_data.shape[1]
if row1 == 0:
h5fw.create_dataset('alldata', dtype="f", shape=(dslen,cols), maxshape=(None, cols) )
if row1+dslen <= len(h5fw['alldata']) :
h5fw['alldata'][row1:row1+dslen,:] = arr_data[:]
else :
h5fw['alldata'].resize( (row1+dslen, cols) )
h5fw['alldata'][row1:row1+dslen,:] = arr_data[:]
row1 += dslen
创建上面读取的源文件:
for fcnt in range(1,4,1):
fname = 'file' + str(fcnt) + '.h5'
arr = np.random.random(50).reshape(10,5)
with h5py.File(fname,'w') as h5fw :
h5fw.create_dataset('data_'+str(fcnt),data=arr)
【讨论】:
当使用方法 2(复制数据'原样')我猜像块和压缩级别这样的 hdf5-dataset 属性也不会被复制。您是否碰巧知道无需指定每个属性即可复制它的方法? 你是对的。方法 2 创建一个新数据集,然后从第一个数据集中复制数据。因此,您必须获取属性,然后在创建新数据集时使用。在我写那个回复的时候,我不知道复制组和数据集的 h5py.copy()
方法。我怀疑使用.copy()
创建的新数据集将继承这些属性——但您应该进行测试以确认。 (它类似于下面的 PyTables copy_children()
方法。)我需要更新我的答案以添加该方法。
注意:我最近发布了一个描述如何执行此操作的答案。详细信息请查看此答案:quickly-extract-tables-to-a-different-hdf5-file【参考方案3】:
对于那些喜欢使用 PyTables 的人,我重新编写了我的 h5py 示例,以展示在 2 个 HDF5 文件之间复制数据的不同方法。这些示例使用与之前相同的示例 HDF5 文件。每个文件只有一个数据集。当您有多个数据集时,您可以在 Pytables 中使用 walk_nodes()
扩展此过程。
所有方法都使用 glob() 来查找以下操作中使用的 HDF5 文件。
方法一:创建外部链接 与 h5py 类似,它在新的 HDF5 文件中创建 3 个组,每个组都有指向原始数据的外部链接。 数据没有被复制。
import tables as tb
with tb.File('table_links_2.h5',mode='w') as h5fw:
link_cnt = 0
for h5name in glob.glob('file*.h5'):
link_cnt += 1
h5fw.create_external_link('/', 'link'+str(link_cnt), h5name+':/')
方法 2:“按原样”复制数据 这会将原始文件中每个数据集的数据复制到使用原始数据集名称的新文件中。数据集对象与源 HDF5 文件的类型相同。在这种情况下,它们是 PyTable 数组(因为所有列都是相同的类型)。 数据集是使用源 HDF5 中的名称复制的,因此每个数据集必须具有不同的名称。数据不会合并到单个数据集中。
with tb.File('table_copy_2.h5',mode='w') as h5fw:
for h5name in glob.glob('file*.h5'):
h5fr = tb.File(h5name,mode='r')
print (h5fr.root._v_children)
h5fr.root._f_copy_children(h5fw.root)
方法 3a:将所有数据合并到 1 个数组中 这会将原始文件中每个数据集的数据复制并合并到新文件中的单个数据集中。同样,数据保存为 PyTables 数组。数据集名称没有限制。首先,我读取数据并附加到一个 Numpy 数组。处理完所有文件后,将 Numpy 数组复制到 PyTables 数组。此过程将 Numpy 数组保存在内存中,因此可能不适用于大型数据集。您可以通过使用 Pytables EArray(可放大数组)来避免此限制。见方法 3b。
with tb.File('table_merge_2a.h5',mode='w') as h5fw:
row1 = 0
for h5name in glob.glob('file*.h5'):
h5fr = tb.File(h5name,mode='r')
dset1 = h5fr.root._f_list_nodes()[0]
arr_data = dset1[:]
if row1 == 0 :
all_data = arr_data.copy()
row1 += arr_data.shape[0]
else :
all_data = np.append(all_data,arr_data,axis=0)
row1 += arr_data.shape[0]
tb.Array(h5fw.root,'alldata', obj=all_data )
方法 3b:将所有数据合并到 1 个可放大的 EArray 中
这类似于上面的方法,但将数据增量保存在 PyTables EArray 中。 EArray.append()
方法用于添加数据。此过程减少了方法 3a 中的内存问题。
with tb.File('table_merge_2b.h5',mode='w') as h5fw:
row1 = 0
for h5name in glob.glob('file*.h5'):
h5fr = tb.File(h5name,mode='r')
dset1 = h5fr.root._f_list_nodes()[0]
arr_data = dset1[:]
if row1 == 0 :
earr = h5fw.create_earray(h5fw.root,'alldata',
shape=(0,arr_data.shape[1]), obj=arr_data )
else :
earr.append(arr_data)
row1 += arr_data.shape[0]
方法四:将所有数据合并到一张表中
此示例突出了 h5py 和 PyTables 之间的区别。在 h5py 中,数据集可以引用 np.arrays
或 np.recarrays
-- h5py 处理不同的 dtype。在 Pytables 中,数组(以及 CArrays 和 EArrays)引用 nd.array
数据,而表引用 np.recarray
数据。此示例说明如何将源文件中的 nd.array
数据转换为适合 Table 对象的 np.recarray
数据。它还展示了如何使用类似于方法 3b 中的EArray.append()
的Table.append()
。
with tb.File('table_append_2.h5',mode='w') as h5fw:
row1 = 0
for h5name in glob.glob('file*.h5'):
h5fr = tb.File(h5name,mode='r')
dset1 = h5fr.root._f_list_nodes()[0]
arr_data = dset1[:]
ds_dt= ([ ('f1', float), ('f2', float), ('f3', float), ('f4', float), ('f5', float) ])
recarr_data = np.rec.array(arr_data,dtype=ds_dt)
if row1 == 0:
data_table = h5fw.create_table('/','alldata', obj=recarr_data)
else :
data_table.append(recarr_data)
h5fw.flush()
row1 += arr_data.shape[0]
【讨论】:
以上是关于如何合并多个 .h5 文件?的主要内容,如果未能解决你的问题,请参考以下文章