在Python中读取v7.3的MAT文件

105

我正在尝试使用以下代码读取matlab文件

import scipy.io
mat = scipy.io.loadmat('test.mat')

然后它给了我以下错误

raise NotImplementedError('Please use HDF reader for matlab v7.3 files')
NotImplementedError: Please use HDF reader for matlab v7.3 files

有没有人遇到过同样的问题,能否提供一些示例代码?

谢谢。


与以下链接相关的编程内容:https://dev59.com/LXNA5IYBdhLWcg3wrPyq - Shai
10个回答

89

我创建了一个小型库,用于加载MATLAB 7.3文件:

pip install mat73

.mat 7.3 文件作为字典加载到 Python 中:

import mat73
data_dict = mat73.loadmat('data.mat')

就是这么简单!


12
最佳答案就在这里。非常感谢您。这类工作可以将很多不必要的杂物从工作中清除出去。 - chupa_kabra
6
你是一位英雄先生! - Aleksejs Fomins
2
为什么这不是标准库的一部分? - ThatNewGuy
1
pip 实际上是最常用的软件包管理器。如果您的工作不允许安装除 Anaconda 默认软件包以外的任何软件包,那我向您表示慰问。您可以尝试在用户文件夹中安装它 pip install mat73 -u,或者只需将 .py 文件下载到您的项目中并导入它,这应该绝对可行。您的公司不可能阻止您这样做。否则,请与您的主管讨论此事。 - skjerns
2
@skjerns 你真是救命稻草!我一直在拼命尝试使用h5py和解引用对象以及其他复杂的东西。这个库完全拯救了我,棒极了!我的唯一抱怨是我在pip中搜索h5、hd5和hdf5,但没有找到它。但我真正想要的是mat文件,所以我应该搜索那个。 - Eric C.
显示剩余5条评论

63

试试使用h5py模块

import h5py
with h5py.File('test.mat', 'r') as f:
    f.keys()

5
是的,但有一个结构体数组,我不知道如何读取它。 - Shan
1
f.keys() 应该会给你返回 'test.mat' 中存储的变量名。你能够访问 f['s'][0].keys() 吗?假设 s 是你所存储的结构体数组的名称,这应该会给你返回 s 的字段列表。 - Shai
2
不行,我无法访问它。更具体来说,我正试图读取以下网站中提供的mat文件:http://ufldl.stanford.edu/housenumbers/。在train.tar.gz文件中有一个名为digitStruct.mat的mat文件。 - Shan
39
这个答案并没有提供足够的背景信息来实际使用.mat文件。当然可以打开这些文件,但是使用scipy.io.loadmat打开的文件被表示为透明的数据结构(具体而言,是字典和numpy数组)。如果回答中还指出了如何实际访问HDF数据结构,则该答案将显着改进。 - aestrivex
3
这段代码将会给你一个字典。通过提取与变量名相关联的键所关联的数据,我们可以获得类似于数组的数据结构。例如 <HDF5 dataset "fv": shape (18000, 9475), type "<f4">。我们可以直接从这个数据结构中访问行或列,或者通过 np.array(data_structure) 轻松地将其转换为 numpy 数组。 - lenhhoxung
显示剩余2条评论

37
import h5py
import numpy as np
filepath = '/path/to/data.mat'
arrays = {}
f = h5py.File(filepath)
for k, v in f.items():
    arrays[k] = np.array(v)

希望能对你有所帮助!除非你使用MATLAB结构体,否则你的数据应该最终存储在arrays字典中。


你观察到了什么问题?你检查过MATLAB(或Octave)是否能打开这个文件吗? - norok2
是的,我可以和他们一起打开它! - Euler_Salter
1
也许它是以旧的MATLAB格式保存的,如果是这种情况,您应该使用 scipy.io.loadmat() https://docs.scipy.org/doc/scipy/reference/generated/scipy.io.loadmat.html#scipy.io.loadmat 这个解决方案适用于MATLAB格式v.7.3及以上。 - norok2
它可以工作,只是原始矩阵的大小为100x256x256x3,但结果的大小为3x256x256x100。最终我不得不使用'swapaxes'。 - Ruchir

17

根据Magu_在相关主题上的回答,请查看软件包hdf5storage,该软件包具有读取v7.3 matlab mat文件的便利函数; 它就像这样简单:

import hdf5storage
mat = hdf5storage.loadmat('test.mat')

非常慢/崩溃了终端。 - s2t2
这样做是不行的,它无法正确加载MATLAB类型(cellstruct)。 - skjerns

12

我看了一下这个问题:https://github.com/h5py/h5py/issues/726。如果你使用-v7.3选项保存mat文件,应该使用以下代码生成密钥列表(在Python 3.x下):

import h5py
with h5py.File('test.mat', 'r') as file:
    print(list(file.keys()))
为了访问变量 a,你需要使用相同的技巧:
with h5py.File('test.mat', 'r') as file:
    a = list(file['a'])

8
根据Scipy cookbook。http://wiki.scipy.org/Cookbook/Reading_mat_files,从Matlab 7.3版本开始,默认情况下使用HDF5格式保存mat文件(除非在保存时使用-vX标志,请参见Matlab中的帮助保存)。可以使用PyTables或h5py包在Python中读取这些文件。目前似乎不支持在mat文件中读取Matlab结构。也许您可以使用Octave重新使用-vX标志进行保存。

据我所知,Octave也不支持v7.3文件。因此,您需要使用足够新的Matlab版本重新保存文件。 - Michael Anderson

4
尽管我已经搜索了数小时,但仍然找不到如何访问Matlab v7.3结构的方法。希望这个部分回答能够帮助某些人,我很乐意看到额外的指针。
因此,从以下内容开始(我认为[0] [0]是由于Matlab将所有东西都给出了维度):
f = h5py.File('filename', 'r')
f['varname'][0][0]

给出的是:< HDF5对象引用 >

将此引用再次传递给 f:

f[f['varname'][0][0]]

这段代码返回一个数组:将其转换为numpy数组并提取值(或者,递归地,另一个<HDF5对象引用>):

np.array(f[f['varname'][0][0]])[0][0]

如果访问磁盘很慢,也许将其加载到内存中会有所帮助。


进一步编辑:经过徒劳的搜索后,我最终的解决方法(但我真的希望有更好的解决方案!)是从Python调用Matlab,这相当容易和快速:

eng = matlab.engine.start_matlab()  # first fire up a Matlab instance
eng.quit()
eng = matlab.engine.connect_matlab()  # or connect to an existing one
eng.sqrt(4.0)
x = 4.0
eng.workspace['y'] = x
a = eng.eval('sqrt(y)')
print(a)
x = eng.eval('parameterised_function_in_Matlab(1, 1)', nargout=1)
a = eng.eval('Structured_variable{1}{2}.object_name')  # (nested cell, cell, object)

我已经为您创建了一个库来完成这个任务:https://github.com/skjerns/mat7.3。 - skjerns

3

这个函数读取由Matlab生成的HDF5 .mat文件,并返回一个嵌套字典的结构,其中包含Numpy数组。由于Matlab按Fortran顺序编写矩阵,因此它还将矩阵和高维数组转置为传统的Numpy顺序arr[...,page,row,col]

import h5py

def read_matlab(filename):
    def conv(path=''):
        p = path or '/'
        paths[p] = ret = {}
        for k, v in f[p].items():
            if type(v).__name__ == 'Group':
                ret[k] = conv(f'{path}/{k}')  # Nested struct
                continue
            v = v[()]  # It's a Numpy array now
            if v.dtype == 'object':
                # HDF5ObjectReferences are converted into a list of actual pointers
                ret[k] = [r and paths.get(f[r].name, f[r].name) for r in v.flat]
            else:
                # Matrices and other numeric arrays
                ret[k] = v if v.ndim < 2 else v.swapaxes(-1, -2)
        return ret

    paths = {}
    with h5py.File(filename, 'r') as f:
        return conv()

有点难追踪但很聪明。 - ThatNewGuy
对我有用。太棒了! - Stücke
有没有办法返回一个数组而不是一个扁平数据和指针的字典?我该如何将字典转换为数组? - Stücke

1
如果您只是在阅读基本的数组和结构体,可以查看vikrantt在类似帖子上的答案。但是,如果您正在使用Matlab table,那么在我看来,最好的解决方案是完全避免save选项。
我创建了一个简单的辅助函数,将Matlab table转换为标准的hdf5文件,并在Python中创建了另一个辅助函数,将数据提取到Pandas DataFrame中。

Matlab 辅助函数

function table_to_hdf5(T, path, group)
%TABLE_TO_HDF5 Save a Matlab table in an hdf5 file format
%
%    TABLE_TO_HDF5(T) Saves the table T to the HDF5 file inputname.h5 at the root ('/')
%    group, where inputname is the name of the input argument for T
%
%    TABLE_TO_HDF5(T, path) Saves the table T to the HDF5 file specified by path at the
%    root ('/') group.
%
%    TABLE_TO_HDF5(T, path, group) Saves the table T to the HDF5 file specified by path
%    at the group specified by group.
%
%%%

if nargin < 2
    path = [inputname(1),'.h5'];  % default file name to input argument
end
if nargin < 3
    group = '';  % We will prepend '/' later, so this is effectively root
end

for field = T.Properties.VariableNames
    % Prepare to write
    field = field{:};
    dataset_name = [group '/' field];
    data = T.(field);
    if ischar(data) || isstring(data)
        warning('String columns not supported. Skipping...')
        continue
    end
    % Write the data
    h5create(path, dataset_name, size(data))
    h5write(path, dataset_name, data)
end

end

Python帮助函数

import pandas as pd
import h5py


def h5_to_df(path, group = '/'):
"""
Load an hdf5 file into a pandas DataFrame
"""
    df = pd.DataFrame()
    with h5py.File(path, 'r') as f:
        data = f[group]
        for k,v in data.items():
            if v.shape[0] > 1:  # Multiple column field
                for i in range(v.shape[0]):
                    k_new = f'{k}_{i}'
                    df[k_new] = v[i]
            else:
                df[k] = v[0]
    return df

重要提示

  • 这仅适用于数字数据。如果您知道如何添加字符串数据,请评论。
  • 如果文件不存在,它将创建该文件。
  • 如果数据已经存在于文件中,则会崩溃。您需要包含适当的逻辑来处理这些情况。

0
我发现最简单的解决办法是启动Matlab,加载这个问题文件,并使用“-v7”标志重新保存它。
load('filename.mat')
save('filename_v7.mat','-v7')

我随后能够使用scipy将'filename_v7.mat'加载到Python中。
只要确保你在Matlab中开始时是一个空的工作区 ;)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接