在 python 中使用 h5py 以 .h5 格式调整和存储数据集

Resizing and storing dataset in .h5 format using h5py in python

我正在尝试使用 python 中的 h5py 包调整数据集的大小并存储新值。我的数据集大小每次都在不断增加,我想使用 resize 函数附加 .h5 文件。但是,我 运行 使用我的方法时出错。变量 dset 是一个数据集数组。

import os
import h5py
import numpy as np

path = './out.h5'
os.remove(path)

def create_h5py(path):
    with h5py.File(path, "a") as hf:
        grp = hf.create_group('left')
        dset = []
        dset.append(grp.create_dataset('voltage', (10**4,3), maxshape=(None,3), dtype='f', chunks=(10**4,3)))
        dset.append(grp.create_dataset('current', (10**4,3), maxshape=(None,3), dtype='f', chunks=(10**4,3)))
        return dset

if __name__ == '__main__':
    dset = create_h5py(path)
    for i in range(3):

        if i == 0:
            dset[0][:] = np.random.random(dset[0].shape) 
            dset[1][:] = np.random.random(dset[1].shape)
        else:
            dset[0].resize(dset[0].shape[0]+10**4, axis=0)
            dset[0][-10**4:] = np.random.random((10**4,3))
            dset[1].resize(dset[1].shape[0]+10**4, axis=0)
            dset[1][-10**4:] = np.random.random((10**4,3))

编辑

感谢 tel 我能够解决这个问题。将 with h5py.File(path, "a") as hf: 替换为 hf = h5py.File(path, "a").

问题

不确定您的其余代码,但您不能在 returns 数据集的函数中使用上下文管理器模式(即 with h5py.File(foo) as bar:)。正如您在问题下的评论中指出的那样,这意味着当您尝试访问数据集时,实际的 HDF5 文件已经关闭。 h5py 中的数据集对象就像文件的实时视图,因此它们需要文件保持打开状态才能使用它们。因此,您会遇到错误。

一个解决方案

始终在托管上下文中(即在 with 子句中)与文件交互是个好主意。如果您的代码抛出错误,上下文管理器将(几乎总是)确保文件已关闭。这有助于避免因崩溃而导致的任何潜在数据丢失。

在您的情况下,您可以通过编写自己的上下文管理器来照管给你归档。

编码实际上非常简单。任何实现 __enter____exit__ 方法的 Python 对象都是有效的上下文管理器。这是一个完整的工作版本:

import os
import h5py
import numpy as np

path = './out.h5'
try:
    os.remove(path)
except OSError: 
    pass

class H5PYManager:
    def __init__(self, path, method='a'):
        self.hf = h5py.File(path, method)

    def __enter__(self):
        # when you call `with H5PYManager(foo) as bar`, the return of this method will be assigned to `bar`
        return self.create_datasets()

    def __exit__(self, type, value, traceback):
        # this method gets called when you exit the `with` clause, including when an error is raised
        self.hf.close()    

    def create_datasets(self):
        grp = self.hf.create_group('left')
        return [grp.create_dataset('voltage', (10**4,3), maxshape=(None,3), dtype='f', chunks=(10**4,3)),
                grp.create_dataset('current', (10**4,3), maxshape=(None,3), dtype='f', chunks=(10**4,3))]

if __name__ == '__main__':
    with H5PYManager(path) as dset:
        for i in range(3):
            if i == 0:
                dset[0][:] = np.random.random(dset[0].shape) 
                dset[1][:] = np.random.random(dset[1].shape)
            else:
                dset[0].resize(dset[0].shape[0]+10**4, axis=0)
                dset[0][-10**4:] = np.random.random((10**4,3))
                dset[1].resize(dset[1].shape[0]+10**4, axis=0)
                dset[1][-10**4:] = np.random.random((10**4,3))

@tel 提供了一个优雅的解决方案。我在他的回答下方的评论中概述了一种更简单的方法。初学者编码(和理解)更简单。基本上,它对@Maxtron 的原始代码进行了一些小改动。修改为:

  • with h5py.File(path, "a") as hf:移动到__main__例程
  • create_h5py(hf)
  • 中通过hf
  • 我在os.remove()之前也加了一个测试,避免h5文件出错 不存在

我的修改建议如下:

import h5py, os
import numpy as np

path = './out.h5'
# test existence of H5 file before deleting
if  os.path.isfile(path):
    os.remove(path)

def create_h5py(hf):
    grp = hf.create_group('left')
    dset = []
    dset.append(grp.create_dataset('voltage', (10**4,3), maxshape=(None,3), dtype='f', chunks=(10**4,3)))
    dset.append(grp.create_dataset('current', (10**4,3), maxshape=(None,3), dtype='f', chunks=(10**4,3)))
    return dset

if __name__ == '__main__':

    with h5py.File(path, "a") as hf:
        dset = create_h5py(hf)
        for i in range(3):

            if i == 0:
                dset[0][:] = np.random.random(dset[0].shape) 
                dset[1][:] = np.random.random(dset[1].shape)
            else:
                dset[0].resize(dset[0].shape[0]+10**4, axis=0)
                dset[0][-10**4:] = np.random.random((10**4,3))
                dset[1].resize(dset[1].shape[0]+10**4, axis=0)
                dset[1][-10**4:] = np.random.random((10**4,3))