如何通过 h5py 读取 v7.3 mat 文件?

Posted

技术标签:

【中文标题】如何通过 h5py 读取 v7.3 mat 文件?【英文标题】:How to read a v7.3 mat file via h5py? 【发布时间】:2013-10-19 02:25:29 【问题描述】:

我有一个由 matlab 创建并存储在 v7.3 格式 mat 文件中的结构数组:

struArray = struct('name', 'one', 'two', 'three', 
                   'id', 1,2,3, 
                   'data', [1:10], [3:9], [0])
save('test.mat', 'struArray', '-v7.3')

现在我想通过 python 使用 h5py 读取这个文件:

data = h5py.File('test.mat')
struArray = data['/struArray']

我不知道如何从struArray中一一获取struct数据:

for index in range(<the size of struArray>):
    elem = <the index th struct in struArray>
    name = <the name of elem>
    id = <the id of elem>
    data = <the data of elem>

【问题讨论】:

您找到了实际的解决方案吗? 我有一个类似的问题,有部分解决方案:***.com/questions/29852481/… 【参考方案1】:

我知道两种解决方案(如果*.mat 文件非常大或非常深,我制作的其中一种效果更好)可以抽象出您与h5py 库的直接交互。

hdf5storage 包,维护良好,旨在帮助将 v7.3 保存的 matfile 加载到 Python 中 my own matfile loader,我写它是为了克服某些问题,即使hdf5storage 的最新版本 (0.2.0) 已经加载了大 (~500Mb) 和/或深数组(我实际上不确定这两者中的哪一个导致问题)

假设您已将这两个包下载到可以将它们加载到 Python 中的位置,您可以看到它们为您的示例 'test.mat' 产生了相似的输出:

In [1]: pyInMine = LoadMatFile('test.mat')
In [2]: pyInHdf5 = hdf5.loadmat('test.mat')  
In [3]: pyInMine()                                                                                                                                          
Out[3]: dict_keys(['struArray'])
In [4]: pyInMine['struArray'].keys()                                                                                                                             
Out[4]: dict_keys(['data', 'id', 'name'])
In [5]: pyInHdf5.keys()                                                                                                                                      
Out[5]: dict_keys(['struArray'])
In [6]: pyInHdf5['struArray'].dtype                                                                                                                          
Out[6]: dtype([('name', 'O'), ('id', '<f8', (1, 1)), ('data', 'O')])
In [7]: pyInHdf5['struArray']['data']                                                                                                                        
Out[7 ]: 
array([[array([[ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.]]),
        array([[3., 4., 5., 6., 7., 8., 9.]]), array([[0.]])]],
      dtype=object)
In [8]: pyInMine['struArray']['data']                                                                                                                            
Out[8]: 
array([[array([[ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.]]),
        array([[3., 4., 5., 6., 7., 8., 9.]]), array([[0.]])]],
      dtype=object)

最大的不同是我的库将 Matlab 中的结构数组转换为 Python 字典,其键是结构的字段,而 hdf5storage 将它们转换为具有各种 dtype 存储字段的 numpy 对象数组。

我还注意到,数组的索引行为与您对 Matlab 方法的期望不同。具体来说,在 Matlab 中,为了获得第二个结构的 name 字段,您需要索引 结构

[Matlab] >> struArray(2).name`
[Matlab] >> 'two'

在我的包中,你必须首先抓取字段,然后然后索引:

In [9]: pyInMine['struArray'].shape                                                                                                                              
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-64-a2f85945642b> in <module>
----> 1 pyInMine['struArray'].shape

AttributeError: 'dict' object has no attribute 'shape'
In [10]: pyInMine['struArray']['name'].shape
Out[10]: (1, 3)
In [11]: pyInMine['struArray']['name'][0,1]
Out[11]: 'two'

hdf5storage 包稍微好一点,它允许您索引结构然后抓取字段,反之亦然,因为结构化的 numpy 对象数组是如何工作的:

In [12]: pyInHdf5['struArray'].shape
Out[12]: (1, 3)
In [13]: pyInHdf5['struArray'][0,1]['name']
Out[13]: array([['two']], dtype='<U3')
In [14]: pyInHdf5['struArray']['name'].shape
Out[14]: (1, 3)
In [15]: pyInHdf5['struArray']['name'][0,1]
Out[15]: array([['two']], dtype='<U3')

同样,这两个包对最终输出的处理略有不同,但总的来说,两者都非常擅长读取 v7.3 matfiles。最后的想法是,在大约 500MB+ 文件的情况下,我发现 hdf5storage 包在加载时挂起,而我的包没有(尽管完成加载仍然需要大约 1.5 分钟)。

【讨论】:

【参考方案2】:

Matlab 7.3 文件格式不是很容易使用 h5py。它依赖于 HDF5 参考,参见。 h5py documentation on references.

>>> import h5py
>>> f = h5py.File('test.mat')
>>> list(f.keys())
['#refs#', 'struArray']
>>> struArray = f['struArray']
>>> struArray['name'][0, 0]  # this is the HDF5 reference
<HDF5 object reference>
>>> f[struArray['name'][0, 0]].value  # this is the actual data
array([[111],
       [110],
       [101]], dtype=uint16)

阅读struArray(i).id

>>> f[struArray['id'][0, 0]][0, 0]
1.0
>>> f[struArray['id'][1, 0]][0, 0]
2.0
>>> f[struArray['id'][2, 0]][0, 0]
3.0

请注意,Matlab 将一个数字存储为一个大小为 (1, 1) 的数组,因此最后一个 [0, 0] 来获取该数字。

阅读struArray(i).data

>>> f[struArray['data'][0, 0]].value
array([[  1.],
       [  2.],
       [  3.],
       [  4.],
       [  5.],
       [  6.],
       [  7.],
       [  8.],
       [  9.],
       [ 10.]])

要读取struArray(i).name,需要将整数数组转换为字符串:

>>> f[struArray['name'][0, 0]].value.tobytes()[::2].decode()
'one'
>>> f[struArray['name'][1, 0]].value.tobytes()[::2].decode()
'two'
>>> f[struArray['name'][2, 0]].value.tobytes()[::2].decode()
'three'

【讨论】:

【参考方案3】:

这确实是 Matlab 7.3 和 h5py 的问题。 我的诀窍是将h5py._hl.dataset.Dataset 类型转换为numpy 数组。 例如,

np.array(data['data'])

将使用'data' 字段解决您​​的问题。

【讨论】:

不起作用。只需在现有的顶部添加另一个阵列层。例如。 array([[&lt;HDF5 object reference&gt;, &lt;HDF5 object reference&gt;, &lt;HDF5 object reference&gt;]], dtype=object) 和现有数据类型为h5py._hl.dataset.Dataset【参考方案4】:

visitvisititems 是查看h5py 文件整体结构的快捷方式:

fs['struArray'].visititems(lambda n,o:print(n, o))

当我在 Octave save -hdf5 生成的文件上运行它时,我得到:

type <HDF5 dataset "type": shape (), type "|S7">
value <HDF5 group "/struArray/value" (3 members)>
value/data <HDF5 group "/struArray/value/data" (2 members)>
value/data/type <HDF5 dataset "type": shape (), type "|S5">
value/data/value <HDF5 group "/struArray/value/data/value" (4 members)>
value/data/value/_0 <HDF5 group "/struArray/value/data/value/_0" (2 members)>
value/data/value/_0/type <HDF5 dataset "type": shape (), type "|S7">
value/data/value/_0/value <HDF5 dataset "value": shape (10, 1), type "<f8">
value/data/value/_1 <HDF5 group "/struArray/value/data/value/_1" (2 members)>
...
value/data/value/dims <HDF5 dataset "dims": shape (2,), type "<i4">
value/id <HDF5 group "/struArray/value/id" (2 members)>
value/id/type <HDF5 dataset "type": shape (), type "|S5">
value/id/value <HDF5 group "/struArray/value/id/value" (4 members)>
value/id/value/_0 <HDF5 group "/struArray/value/id/value/_0" (2 members)>
...
value/id/value/_2/value <HDF5 dataset "value": shape (), type "<f8">
value/id/value/dims <HDF5 dataset "dims": shape (2,), type "<i4">
value/name <HDF5 group "/struArray/value/name" (2 members)>
...
value/name/value/dims <HDF5 dataset "dims": shape (2,), type "<i4">

这可能与 MATLAB 7.3 生成的不同,但它给出了结构复杂性的概念。

更精细的回调可以显示值,并且可以作为重新创建 Python 对象(字典、列表等)的起点。

def callback(name, obj):
    if name.endswith('type'):
        print('type:', obj.value)
    elif name.endswith('value'):
        if type(obj).__name__=='Dataset':
            print(obj.value.T)  # http://***.com/questions/21624653
    elif name.endswith('dims'):
        print('dims:', obj.value)
    else:
        print('name:', name)

fs.visititems(callback)

产生:

name: struArray
type: b'struct'
name: struArray/value/data
type: b'cell'
name: struArray/value/data/value/_0
type: b'matrix'
[[  1.   2.   3.   4.   5.   6.   7.   8.   9.  10.]]
name: struArray/value/data/value/_1
type: b'matrix'
[[ 3.  4.  5.  6.  7.  8.  9.]]
name: struArray/value/data/value/_2
type: b'scalar'
0.0
dims: [3 1]
name: struArray/value/id
type: b'cell'
name: struArray/value/id/value/_0
type: b'scalar'
1.0
...
dims: [3 1]
name: struArray/value/name
type: b'cell'
name: struArray/value/name/value/_0
type: b'sq_string'
[[111 110 101]]
...
dims: [3 1]

【讨论】:

【参考方案5】:

我会首先启动解释器并在struarray 上运行help。它应该为您提供足够的信息来帮助您入门。否则,您可以通过printing __dict__ 属性转储任何 Python 对象的属性。

【讨论】:

【参考方案6】:

很抱歉,但我认为从 Matlab 外部获取单元格/结构的内容将非常具有挑战性。如果您查看生成的文件(例如使用 HDFView),您会看到有很多交叉引用并且没有明显的方法可以继续。

如果您坚持使用简单的数值数组,它就可以正常工作。如果您有包含数值数组的小元胞数组,您可以将它们转换为单独的变量(即 cellcontents1、cellcontents2 等),这些变量通常只有几行,可以直接保存和加载。因此,在您的示例中,我将使用 vars name1, name2, name3, id1, id2, id3 ... 等保存一个文件。

编辑:您在问题中指定了 h5py,这就是我的回答,但值得一提的是,使用 scipy.io.loadmat 您应该能够将原始变量转换为 numpy 等效项(例如对象数组)。

【讨论】:

谢谢!几天来我一直在努力解决这个问题。我总是得到像&lt;HDF5 object reference&gt; 这样的东西,而不是真正的价值。但是,scipy.io.loadmat 不适用于 v7.3 格式的 mat 文件。

以上是关于如何通过 h5py 读取 v7.3 mat 文件?的主要内容,如果未能解决你的问题,请参考以下文章

使用 h5py 读取 matlab .mat 文件

python读取mat(v7.3)文件中的cell以及struct

Python 之 h5py 读取 matlab 中 .mat 文件 cell 方法浅析

python写入和读取h5、pkl、mat 文件

如何打开未使用 -v7.3 标志保存的大型 .mat 文件?

如何使用 h5py 通过 szip 压缩访问 HDF5 数据集