网上提供的所有内容都太复杂了。我有一个很大的数据库,所以我把它分成几个部分导出。现在我有三个.h5文件,我想将它们合并成一个.h5文件以进行进一步的工作。我该怎么做呢?
visititems()
扩展此过程。glob()
来查找下面操作中使用的HDF5文件。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():
h5fr.copy(obj, h5fw)
方法二b:直接复制数据
(这是我最初的答案,当时我还不知道.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)
方法三a:将所有数据合并为一个固定大小的数据集
这将把原始文件中每个数据集的数据复制和合并到新文件的单个数据集中。 在本例中,对数据集名称没有任何限制。另外,我最初创建了一个大型数据集,并且不调整大小。这假设有足够的行来容纳所有合并的数据。生产工作中应添加测试。
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]
第三种方法b:将所有数据合并到一个可调整大小的数据集中
这与上述方法类似。不过,我创建了一个可调整大小的数据集,并根据读取和添加的数据量进行扩大。
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)
.copy()
方法可以复制组和数据集。我猜测使用.copy()
创建的新数据集将继承这些属性--但您应该进行测试以确认。(这类似于下面的PyTables copy_children()
方法。)我需要更新我的答案以添加该方法。 - kcw78visititems()
函数用于多个数据集,但我还需要考虑 HDF5 文件的多个键。 - Hermi.visititems()
。我找到了一个更好的解决方案,使用了生成器。 - kcw78h5r.copy(obj, h5fw)
中有一个小错别字吗?应该是 h5fr.copy(obj, h5fw)
,变量名应该有一个 f
字符,对吧? - Hakan Baba对于那些更喜欢使用PyTables的人,我重新制作了我的h5py示例,展示了在两个HDF5文件之间复制数据的不同方法。这些示例使用与以前相同的示例HDF5文件。每个文件只有一个数据集。当您有多个数据集时,可以使用Pytables中的walk_nodes()
扩展此过程。
所有方法都使用glob()来查找下面操作中使用的HDF5文件。
方法1:创建外部链接
类似于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+':/')
方法二:直接复制数据
此方法将从原始文件中每个数据集中复制数据到新文件中,使用原始数据集名称。数据集对象与源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 Array格式。数据集名称没有限制。首先读取数据并追加到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]
方法 4:将所有数据合并到一个表中
此示例突出了h5py和PyTables之间的区别。在 h5py 中,数据集可以引用 np.arrays
或 np.recarrays
-- h5py 处理不同的 dtypes。在 Pytables 中,数组(和 CArrays 和 EArrays)引用 nd.array
数据,而表引用 np.recarray
数据。此示例演示了如何将源文件中的 nd.array
数据转换为适合于 Table 对象的 np.recarray
数据。它还展示了如何使用 Table.append()
类似于方法 3b 中的 EArray.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]
本回答是为了解决@Hermi在2022年3月11日提出的合并来自多个文件的几个组和数据集的请求。一般情况下,这是一个复杂的问题--需要进行大量的错误检查,以确保一致的组名称、数据集名称和数据集属性(dtype和形状)。此外,数据集可以在多个方向上被“扩展”以容纳合并的数据(没有一个正确的答案)。
下面的代码实现了以下功能:
h5py_dataset_iterator()
,该生成器来自如何区分HDF5数据集和组。由于.visititems()
的返回和yield行为,这个生成器是一个更好的解决方案。下面是代码:
import h5py
import glob
# Ref: https://dev59.com/fFsX5IYBdhLWcg3wB7n3#34401029
# with slight modifications
def h5py_dataset_iterator(g, prefix=''):
for name, h5obj in g.items():
path = '{}/{}'.format(prefix, name)
if isinstance(h5obj, h5py.Dataset): # test for dataset
yield (h5obj, path)
elif isinstance(h5obj, h5py.Group): # test for group (go down)
yield from h5py_dataset_iterator(h5obj, prefix=path)
with h5py.File('merged_h5_data.h5', mode='w') as h5w:
for h5source in glob.iglob('file*.h5'):
print(f'\nWorking on file: {h5source}')
with h5py.File(h5source, mode='r') as h5r:
for (dset, path) in h5py_dataset_iterator(h5r):
print(f'Copying dataset from: {path}')
ds_obj = h5r[path]
arr_dtype = ds_obj.dtype
arr_shape = ds_obj.shape
# If dataset doesn't exist, create new datset and copy data
# Note: we can't use .copy() method b/c we are changing shape and maxshape
if path not in h5w:
h5w.create_dataset(path, data=ds_obj,
shape=arr_shape+(1,), maxshape=arr_shape+(None,))
else:
# Check for compatiable dtype and shape
ds_dtype = h5w[path].dtype
ds_shape = h5w[path].shape
# If dataset exists and is compatibale, resize datset and copy data
if ds_dtype == arr_dtype and ds_shape[:-1] == arr_shape:
new_shape = ds_shape[0:-1] + (ds_shape[-1]+1,)
h5w[path].resize(new_shape)
h5w[path][...,-1] = ds_obj
# Add attribute to dataset with source file name:
h5w[path].attrs[f'File for index {h5w[path].shape[-1]-1} '] = h5source
用于创建上述使用的源文件的代码:
示例1:简单模式(2个组,每个组有3个数据集)
import h5py
import numpy as np
for fcnt in range(1,4,1):
fname = 'file' + str(fcnt) + '.h5'
with h5py.File(fname,'w') as h5fw:
for gcnt in range(1,3,1):
grp = h5fw.create_group(f'group_{gcnt}')
for dcnt in range(1,4,1):
arr = np.random.randint(0,high=255,size=100,dtype=np.uintc).reshape(10,10)
grp.create_dataset(f'dataset_{dcnt}',data=arr)
示例2:高级模式(包含组和数据集的3个层级)
import h5py
import numpy as np
ds_list = ['/dataset_1', '/dataset_2',
'/group_1/group_11/dataset_1', '/group_1/group_11/dataset_2',
'/group_1/group_12/dataset_1', '/group_1/group_12/dataset_2',
'/group_2/dataset_1', '/group_2/dataset_2',
'/group_3/group_31/group_311/dataset_1',
'/group_3/group_31/group_312/dataset_1']
for fcnt in range(1,4,1):
fname = 'cfile' + str(fcnt) + '.h5'
with h5py.File(fname,'w') as h5fw:
for name in ds_list:
arr = np.random.randint(0,high=255,size=100,dtype=np.uintc).reshape(10,10)
h5fw.create_dataset(name, data=arr)
h5py_dataset_iterator
的有效替代方案? - HermiGroup.walk_nodes()
)。 - kcw78
h5py
库编写一个简单的 Python 代码。 - Tom de Geus