使用astype在H5py中创建对HDF数据集的引用

3 投票
2 回答
1665 浏览
提问于 2025-04-18 16:49

根据h5py的文档,我了解到可以使用astype方法将HDF数据集转换成其他类型。这个方法会返回一个上下文管理器,可以在需要的时候进行转换。

不过,我想先读取一个存储为uint16类型的数据集,然后把它转换成float32类型。之后,我还想在另一个函数中提取这个数据集的不同切片,并且这些切片也要是float32类型。文档中是这样解释的:

with dataset.astype('float32'):
   castdata = dataset[:]

这样做会导致整个数据集被读取并转换为float32,这并不是我想要的。我希望能够引用这个数据集,但转换成float32,就像numpy.astype那样。我该如何创建一个对.astype('float32')对象的引用,以便可以传递给另一个函数使用呢?

举个例子:

import h5py as HDF
import numpy as np
intdata = (100*np.random.random(10)).astype('uint16')

# create the HDF dataset
def get_dataset_as_float():
    hf = HDF.File('data.h5', 'w')
    d = hf.create_dataset('data', data=intdata)
    print(d.dtype)
    # uint16

    with d.astype('float32'):
    # This won't work since the context expires. Returns a uint16 dataset reference
       return d

    # this works but causes the entire dataset to be read & converted
    # with d.astype('float32'):
    #   return d[:]

此外,似乎只有在访问数据元素时,astype的上下文才会生效。这意味着:

def use_data():
   d = get_data_as_float()
   # this is a uint16 dataset

   # try to use it as a float32
   with d.astype('float32'):
       print(np.max(d))   # --> output is uint16
       print(np.max(d[:]))   # --> output is float32, but entire data is loaded

那么,难道没有类似numpy的方式来使用astype吗?

2 个回答

0

astype的文档似乎暗示它的主要目的是将数据读取到一个新的位置。因此,如果你打算在不同的地方多次使用浮点数转换,return d[:]是最合理的选择。

如果你知道你需要转换的目的,并且只需要一次,你可以调整一下,做成类似这样的:

def get_dataset_as_float(intdata, *funcs):
    with HDF.File('data.h5', 'w') as hf:
        d = hf.create_dataset('data', data=intdata)
        with d.astype('float32'):
            d2 = d[...]
            return tuple(f(d2) for f in funcs)

无论如何,你要确保在离开这个函数之前关闭hf,否则以后会遇到问题。

一般来说,我建议将数据的转换和数据集的加载/创建完全分开,并将数据集作为函数的一个参数传递。

上面的内容可以这样调用:

In [16]: get_dataset_as_float(intdata, np.min, np.max, np.mean)
Out[16]: (9.0, 87.0, 42.299999)
1

d.astype() 会返回一个 AstypeContext 对象。如果你查看 AstypeContext 的源代码,你会更清楚地了解发生了什么:

class AstypeContext(object):

    def __init__(self, dset, dtype):
        self._dset = dset
        self._dtype = numpy.dtype(dtype)

    def __enter__(self):
        self._dset._local.astype = self._dtype

    def __exit__(self, *args):
        self._dset._local.astype = None

当你进入 AstypeContext 时,你的数据集的 ._local.astype 属性会被更新为你想要的新类型,而当你退出这个上下文时,它又会恢复到原来的值。

所以,你可以通过这种方式实现你想要的效果:

def get_dataset_as_type(d, dtype='float32'):

    # creates a new Dataset instance that points to the same HDF5 identifier
    d_new = HDF.Dataset(d.id)

    # set the ._local.astype attribute to the desired output type
    d_new._local.astype = np.dtype(dtype)

    return d_new

现在,当你从 d_new 读取数据时,你会得到 float32 的 numpy 数组,而不是 uint16 的:

d = hf.create_dataset('data', data=intdata)
d_new = get_dataset_as_type(d, dtype='float32')

print(d[:])
# array([81, 65, 33, 22, 67, 57, 94, 63, 89, 68], dtype=uint16)
print(d_new[:])
# array([ 81.,  65.,  33.,  22.,  67.,  57.,  94.,  63.,  89.,  68.], dtype=float32)

print(d.dtype, d_new.dtype)
# uint16, uint16

需要注意的是,这并不会更新 d_new.dtype 属性(这个属性似乎是不可变的)。如果你还想改变 dtype 属性,你可能需要创建一个 h5py.Dataset 的子类来实现。

撰写回答