Python数据分析与处理(一):读取不同格式.mat文件的具体方法【超详细】
MATLAB的.mat文件格式分为HDF5(v7.3)和经典MAT格式(v7/v7.2及更早)。本文介绍了两种格式的区别、检测方法和适用工具:HDF5格式适合用h5py按需读取,经典格式推荐scipy.io.loadmat。文章提供了实用代码片段,包括版本检测、数据读取和编码转换,并总结了常见问题的解决方案,如内存优化、字符串解码和跨语言数据兼容性处理。最后给出了针对不同场景的工作流建议,帮助用户
文章目录
前言
在科研与工程实践中,MATLAB 的 .mat 文件是不同语言与工具之间交换矩阵、结构体、cell 等数据的常见格式。随着 MATLAB 在 2006 年引入基于 HDF5 的 -v7.3 格式(从 R2006b / MATLAB 7.3 起可用),Python 端处理 .mat 文件出现了两类截然不同的工作流:一类是“经典” MAT-file(v7 / v7.2 / 更早)适合用 scipy.io.loadmat;另一类是 HDF5(v7.3)适合用 h5py(或专门库 hdf5storage、mat73 等)。本文详细探讨了这两种工作流的区别、适用场景和潜在挑战,附实用代码片段与检查清单,方便科研/工程日常使用。
一、 MAT-file 版本快速概览
- v7.3(HDF5)格式:从 MATLAB v7.3 = Release R2006b 开始支持(可用
save(...,'-v7.3')保存)。本质上是 HDF5 文件,适合保存 >2GB 的单变量并支持按需读取。 - v7.2 及之前(经典 MAT):对应 R2006a 及更早版本生成的“经典” MAT 格式,
scipy.io.loadmat对这类格式支持最好。 - 默认格式:Matlab R2019b及之前版本默认为
-v7.3格式,Matlab R2020a及之后版本虽然也默认为-v7.3格式,但也支持之前的格式。如果你希望明确地指定保存为-v7.3格式,可以在save函数中使用-v7.3选项。例如:
save('filename.mat', 'variableName', '-v7.3');
二、 如何判断 .mat 文件是 v7.3(HDF5)还是经典格式
最简单也是最可靠的方法是读文件看开头(HDF5 文件以 \x89HDF 开头):
def is_mat_v73(path):
with open(path,'rb') as f:
head = f.read(8)
return head.startswith(b'\x89HDF')
- 如果返回
True:通常就是 v7.3(HDF5)。推荐用h5py或hdf5storage来读取。 - 如果返回
False:可能是经典 MAT(v7/v7.2/更早),可以用scipy.io.loadmat读取(更方便地将 MATLAB struct/cell 映射到 Python 对象/列表)。
三、scipy.io.loadmat:什么时候用、常用参数与注意事项
适用场景:经典 MAT(v7 / v7.2 及更早)且数据量不会导致内存溢出时。
示例:
from scipy.io import loadmat
data = loadmat('file.mat',
squeeze_me=True, # 去掉单例维度
struct_as_record=False, # 用更易用的对象形式返回 struct
simplify_cells=True) # 尽量把 cell 转为 list/ndarray(较新 SciPy 支持)
# 访问变量
X = data['X']
常见参数与作用
squeeze_me=True:压掉多余维度(如(1, N, 1))。struct_as_record=False:把 MATLAB struct 转成更可读的对象/嵌套结构,而不是 numpy record。simplify_cells=True:(较新 SciPy)把 cell 数组尽量简化成 Python list/ndarray。
限制
- 不能读取 v7.3 文件 —— 会抛出提示要求用 HDF5 读器。
- 一次性把整个文件加载到内存中,遇大文件(尤其大数组)会内存压力很大。
四、h5py:什么时候用、基本用法与陷阱
适用场景:.mat 是 v7.3(HDF5)或你需要按需读取非常大的数据集时。
示例:
import h5py
with h5py.File('file.mat', 'r') as f:
print(list(f.keys())) # 顶层变量名(注意:MATLAB 在 HDF5 中的存储结构和命名风格)
dset = f['myVar']
print(dset.shape, dset.dtype)
arr = dset[:] # 一次性读取为 numpy array
part = dset[:, :100] # 切片读取(按需)
注意事项与常见坑
- MATLAB 在 HDF5 中保存字符串、char、cell、对象引用等时使用的内部约定会使
h5py直接读取后返回bytes、引用或复杂结构,需要额外解码/解析。 - 直接用
h5py写入或改写 v7.3.mat很容易破坏 MATLAB 能读回的元信息(MATLAB 期望特定的属性/元数据)。因此建议写 v7.3 文件时优先使用hdf5storage或 MATLAB 自身的save -v7.3,而不是手动用h5py构造复杂结构。 - 若只读并需要兼容 MATLAB 的高层语义(比如 cell、struct、对象):
hdf5storage通常比裸h5py更方便,因为它做了 MATLAB <-> HDF5 的映射与编码处理。
五、从 Python 写回 .mat:推荐做法
- 写经典 MAT(v5/v7 等):使用
scipy.io.savemat,例如savemat('out.mat', {'X': arr}, format='5')。这对于与老版本 MATLAB 或需要最大兼容性的场景通常最稳妥。 - 写 v7.3(HDF5):如果目标是让 MATLAB 用户能顺利打开结果文件,优先方法是让 MATLAB 来生成(
save(...,'-v7.3')),或在 Python 端使用hdf5storage(一个基于h5py的高层库,它会写入 MATLAB 需要的元信息)。裸用h5py写 v7.3 在很多情况下会导致 MATLAB 无法读取或读出错误的对象类型。
五、常见坑及对应解决办法(汇总)
-
报错
Please use HDF reader for matlab v7.3 files
→ 检测文件是否为 HDF5(见上方is_mat_v73),改用h5py或hdf5storage/mat73。 -
struct / cell 读取后嵌套难以直接使用
→loadmat(..., struct_as_record=False, squeeze_me=True, simplify_cells=True);或在用h5py时手动解析 HDF5 结构引用并转换为 dict/list。 -
字符串显示为 bytes 或编码混乱
→ 对字节进行decode('utf-8')(或视具体编码),或用hdf5storage让库处理编码。注意:有时 MATLAB 在 v7.3 中会用 UTF-16 编码,需对应 decode。 -
维度/行列顺序问题(MATLAB 为列主)
→ 多数情况下 NumPy 会得到正确的形状,但在 reshape/flatten 或跨语言序列化时应注意使用order='F'或用.T做转置。 -
内存不足
→loadmat会一次性载入整个文件;若文件非常大且是 v7.3,优先用h5py做按需切片读取。
六、实用工作流(推荐)
-
我有一个
.mat文件,不知道版本- 先用
is_mat_v73(path)检测头。 - 如果是 v7.3(HDF5)→ 用
h5py展开list(f.keys()),按需读取需要的数据集;若想得到 MATLAB 风格的 high-level object(cell/struct),用hdf5storage或手写解析逻辑。 - 如果不是 v7.3 → 用
scipy.io.loadmat(..., squeeze_me=True, struct_as_record=False, simplify_cells=True)。
- 先用
-
我要把 Python 数据写给 MATLAB 用户读
- 若文件尺寸较小且简单(数组、基本结构):用
scipy.io.savemat写经典 MAT。 - 若确实需要 v7.3(比如单变量 > 2GB):在 MATLAB 里保存
-v7.3,或者在 Python 用hdf5storage(慎用裸h5py写复杂对象)。
- 若文件尺寸较小且简单(数组、基本结构):用
七、常用代码片段合集(可直接拷贝)
检测是否 v7.3(HDF5)
def is_mat_v73(path):
with open(path,'rb') as f:
head = f.read(8)
return head.startswith(b'\x89HDF')
用 scipy.io.loadmat 读取经典 MAT
from scipy.io import loadmat
data = loadmat('file.mat', squeeze_me=True, struct_as_record=False, simplify_cells=True)
# 访问:
# X = data['X']
# some_struct = data['myStruct'].fieldname # 视 loadmat 返回类型而定
用 h5py 读取 v7.3(HDF5)
import h5py
with h5py.File('file.mat', 'r') as f:
print(list(f.keys()))
dset = f['myVar']
print(dset.shape, dset.dtype)
arr = dset[:] # 读取全部
part = dset[0:100, :] # 分块读取以节省内存
把 bytes 解码为字符串
import numpy as np
# 假设 arr 是 dtype 'S' 或含 bytes 的数组
strs = np.array([x.decode('utf-8') for x in arr.ravel()]).reshape(arr.shape)
总结
- 判断
.mat文件版本是关键:v7.3 = HDF5(从 R2006b / MATLAB 7.3 起可写),之前的通常是经典 MAT(可用scipy.io.loadmat处理)。用文件头判断是最简单的办法。 - 对于经典 MAT(v7 / v7.2 / 更早),
scipy.io.loadmat最方便(带若干参数可改善 struct/cell 的表现);但它会将整个文件一次性读入内存。 - 对于v7.3(HDF5),
h5py是阅读的大杀器——支持按需读取 / 切片,从而处理非常大的数据集;但直接写入 v7.3(尤其复杂对象)用h5py很容易出错,优先选择hdf5storage或让 MATLAB 来写入-v7.3。 - 遇到 struct/cell/字符串或对象引用问题时,
hdf5storage常常比裸h5py更省力;而scipy.io.loadmat的参数(squeeze_me,struct_as_record=False,simplify_cells=True)能显著改善经典 MAT 的可用性。
更多推荐
所有评论(0)