如何用H5py在HDF5中存储同一数据的双视图而不重复数据?
问题描述
我正在使用H5py与numpy操作HDF5文件,希望为同一数据提供两种带有不同元数据的视图,尝试通过存储一维数组的引用而非复制数据来实现。
具体场景:
- 有
num_observers个一维数据流,每个流包含num_samples个值(如温度计的温度读数),需要将每个数据流作为独立数据集存储,并添加对应元数据属性。 - 某些场景下需要将所有数据流以
(num_observers, num_samples)维数组的形式读取,且该数组数据集的元数据与单个数据流的元数据不同。
当前代码实现后,文件体积是仅存储单个数据流时的两倍,原以为用了硬链接,但显然数据被复制了:
num_observers = get_num_observers() num_samples = get_num_samples() with h5py.File(filename, "w") as f: grp_observations = f.create_group("observations") grp_indiv = f.create_group("observations/as_individual") grp_ndarray = f.create_group("observations/as_ndarray") dset_ndarray = grp_ndarray.create_dataset( "observation_ndarray", (num_observers, num_samples), dtype="float" ) set_dset_ndarray_metadata() observer_ids = get_sorted_observers() for observer_index, observer_id in enumerate(observer_ids): observer_data = get_data_stream(observer_id) observer_metadata = get_metadata(observer_id) latest_individual_dataset = grp_indiv.create_dataset( observer_id, data=observer_data, dtype="float" ) set_latest_individual_dset_metadata(observer_metadata) # 此处希望引用数据而非复制,但实际还是复制了 dset_ndarray[observer_index] = latest_individual_dataset
疑问:是否因为dset_ndarray创建时已分配大小,后续硬链接操作无法改变?如何实现两种数据视图且不使文件体积翻倍?
解决方案
1. 原有方法复制数据的原因
你当前的代码并没有实现数据共享:
dset_ndarray[observer_index] = latest_individual_dataset这行是把单个数据集的数据复制到预分配的dset_ndarray存储空间中,本质是两次写入数据,所以文件体积翻倍。- HDF5硬链接是让多个路径指向同一个数据对象,但硬链接的数据集会共享元数据(属性),无法满足你“两种视图有不同元数据”的需求。
2. 正确方案:使用虚拟数据集(VDS)
HDF5的虚拟数据集(Virtual Dataset, VDS) 是专门实现逻辑数据视图的工具,它不复制原始数据,仅存储数据的映射关系,同时可以为VDS添加独立的元数据,完美匹配你的需求。
3. 修改后的代码实现
num_observers = get_num_observers() num_samples = get_num_samples() with h5py.File(filename, "w") as f: grp_observations = f.create_group("observations") grp_indiv = f.create_group("observations/as_individual") grp_ndarray = f.create_group("observations/as_ndarray") observer_ids = get_sorted_observers() # 先创建所有单个观察者的数据集,存储数据并添加元数据 virtual_sources = [] for observer_index, observer_id in enumerate(observer_ids): observer_data = get_data_stream(observer_id) observer_metadata = get_metadata(observer_id) # 创建单个数据集 dset_indiv = grp_indiv.create_dataset( observer_id, data=observer_data, dtype="float" ) set_latest_individual_dset_metadata(observer_metadata) # 记录VDS需要的源信息:数据集、切片范围 vsource = h5py.VirtualSource(dset_indiv) virtual_sources.append(vsource) # 创建虚拟数据集,将所有单个数据集拼接成二维数组 layout = h5py.VirtualLayout(shape=(num_observers, num_samples), dtype="float") for idx, vsource in enumerate(virtual_sources): layout[idx, :] = vsource dset_vds = grp_ndarray.create_dataset("observation_vds", virtual_layout=layout) # 给VDS添加独立的元数据 set_dset_ndarray_metadata(dset_vds)
4. 关键说明
- VDS本身不存储实际数据,只存储从原始数据集读取数据的映射规则,文件体积和仅存储单个数据流时几乎一致。
- 单个数据集和VDS是独立对象,各自拥有专属的元数据属性,互不影响。
- 读取VDS的方式和普通数据集完全一致:
data = f["observations/as_ndarray/observation_vds"][:],HDF5会自动从对应原始数据集读取数据。
内容的提问来源于stack exchange,提问作者rtclay
相关产品推荐
相关产品推荐

