HOWTO · Python

在 Python 中讀取 Matlab mat 檔案

本教程演示瞭如何在 Python 中讀取和開啟 Matlab mat 檔案

本頁內容

MATLAB 會將工作區變數儲存在 .mat 檔案中,而 Python 並沒有內建的方式可以開啟它們——你需要依據 MAT 檔案版本選擇合適的讀取函式庫。本指南說明如何辨識檔案格式,並使用 scipy.io、h5py、mat4py 或 matlab.engine API 載入其中的變數,同時解釋每種方法的適用界限。

依 MAT 檔案版本選擇讀取工具

MATLAB 歷來使用過兩種檔案配置。MAT 檔案第 5 版到 7.2 版採用 MATLAB 自有的二進位格式,開頭是一個以 MATLAB 起始的 128 位元組文字標頭;更早期的 MATLAB 4 檔案使用相同的經典配置,但沒有描述性標頭——它們直接以資料元素開頭。scipy.io.loadmat 可以讀取整個經典系列,無論有無標頭。以 -v7.3 儲存的檔案是 HDF5 檔案:它們保留一小段 MATLAB 使用者區塊,但資料以 HDF5 資料集的形式儲存,這正是 h5py 所開啟的內容。NumPy 本身無法開啟任一種配置——它只能把已經載入的資料集轉換成陣列。

不需安裝任何套件,只要檢視檔案的開頭位元組就能辨識檔案。執行以下範例,它會在暫存資料夾中各寫入一個常見類型的檔案,並依標頭進行分類(範例程式碼中的訊息與輸出文字維持英文原文):

本節中的程式碼以及顯示的標籤或輸出仍為英文。

import os
import tempfile

folder = tempfile.mkdtemp()
classic = os.path.join(folder, "classic.mat")
modern = os.path.join(folder, "v73.mat")

with open(classic, "wb") as f:
    f.write(b"MATLAB 5.0 MAT-file, Platform: GLNXA64, Created on: Thu Oct 29 2026".ljust(128, b" "))

with open(modern, "wb") as f:
    f.write(b"MATLAB 7.3 MAT-file, Platform: GLNXA64, HDF5 schema 1.00".ljust(512, b" "))
    f.write(b"\x89HDF\r\n\x1a\n")

def mat_kind(path):
    with open(path, "rb") as f:
        head = f.read(128)
        f.seek(512)
        signature = f.read(8)
    if b"HDF5" in head or signature.startswith(b"\x89HDF"):
        return "MATLAB 7.3 (HDF5): read with h5py"
    if head.startswith(b"MATLAB"):
        return "MAT-file 7.2 or older: read with scipy.io.loadmat"
    return "no v5+ header or HDF5 signature — possibly a MATLAB 4 file: try scipy.io.loadmat"

print(mat_kind(classic))
print(mat_kind(modern))
MAT-file 7.2 or older: read with scipy.io.loadmat
MATLAB 7.3 (HDF5): read with h5py

真正的 7.3 檔案會在標頭文字中提到 HDF5,並在使用者區塊之後帶有 HDF5 簽章;而 5–7.2 版檔案的標頭只是單純以 MATLAB 開頭。這項檢查僅依這兩個標記分類:MATLAB 4 檔案兩者皆無,因此會落到後備訊息——但 scipy.io.loadmat 仍然可以開啟它,所以請把這個結果視為直接嘗試 loadmat 的提示,而非檔案無法讀取的證明。一般原則是:檔案為 7.2 版或更舊時優先使用 scipy.io.loadmat,7.3 檔案使用 h5py,想從經典檔案取得純 Python 列表而非 NumPy 陣列時選用 mat4py,只有當 MATLAB 本身已安裝且需要複雜型別的完整保真度時才使用 matlab.engine。變數進入 Python 之後,pandas 可以將其匯出為 CSV 或 Excel;若日後需要,也可以用 scipy.io.savemat 或 HDF5 資料集寫回 .mat 檔案。

以下各節使用 sample.mat(經典檔案)與 sample_v73.mat(7.3 檔案),並假設其中含有名為 measurements 的變數。這些程式碼片段與相依套件有關:它們假設指定的套件已安裝,因此請在自己的環境中執行,而非在裸直譯器中執行。

使用 SciPy 讀取 MATLAB 檔案

先用 pip install scipy 安裝函式庫,然後呼叫 scipy.io.loadmat("sample.mat")。它會回傳一個字典:除了你的變數之外,還帶有 __header__、__version__ 和 __globals__ 這些詮釋資料鍵;每個 MATLAB 變數都會以其工作區名稱成為一個 NumPy 陣列,因此 data["measurements"] 可以直接取得該變數。

當形狀看起來不對時,有兩個選項值得了解。將 squeeze_me=True 傳給 loadmat 會摺疊 MATLAB 的單一維度,讓純量以純量形式回傳;搭配 struct_as_record=False 則可以把 struct 欄位當作屬性存取,而不是巢狀陣列元素。若要寫回結果,scipy.io.savemat("out.mat", {"measurements": data["measurements"]}) 會產生一個經典 MAT 檔案。

版本限制是最重要的限制:loadmat 僅支援 7.2 及以前的 MAT 檔案格式。遇到 7.3 檔案時,它會回報格式不受支援,因為其內容是 HDF5——這種檔案需要下一節的做法。如果你能控制 MATLAB 端,以 save data.mat -v7 儲存即可產生 loadmat 能接受的檔案。

使用 h5py 讀取 MATLAB 7.3 檔案

-v7.3 檔案是帶有 MATLAB 使用者區塊的 HDF5 檔案,因此只要 pip install h5py 就能開啟它。h5py.File("sample_v73.mat", "r") 以讀取模式開啟檔案,list(f.keys()) 會列出頂層群組——每個 MATLAB 變數各一個,外加一個儲存共用 cell 與 struct 內容的 #refs# 群組。

讀取 f["measurements"][()] 會將資料集具體化為 NumPy 陣列;這裡 NumPy 只是負責轉換資料,因為檔案是由 h5py 開啟的。實務上有兩個配置細節要注意。MATLAB 以 column-major 順序寫入陣列,因此二維資料集讀回來時常會是轉置的,使用 f["measurements"][()].T 即可還原 MATLAB 的方向。Cell 陣列與 struct 會以指向 #refs# 的 HDF5 物件參照形式出現,所以需要用 f[ref] 對每個項目解參照,而不是直接讀取。

使用 mat4py 讀取 MATLAB 檔案

pip install mat4py 提供一個針對經典 MAT 檔案、較精簡的 API。執行 from mat4py import loadmat 之後,呼叫 loadmat("sample.mat") 會回傳一個普通字典,其中數值陣列是巢狀的 Python 列表,struct 則會變成巢狀字典——當你想以純 Python 型別取得資料、又不想引入 NumPy 時相當方便。

請把 mat4py 視為經典格式的讀取工具:它無法開啟 7.3 的 HDF5 檔案,而且深度巢狀或特殊的 MATLAB 結構可能無法順利對應,因此它並非適用於所有 MAT 檔案的替代品。在以複雜檔案為基礎建構之前,請先對照 scipy.io 驗證其轉換結果。

使用 MATLAB Engine API 讀取 MATLAB 檔案

當 MATLAB 本身已安裝時,其 Python 版 Engine API 是保真度最高的選項。以 pip 安裝 matlabengine(或執行 MATLAB 隨附的安裝程式)即可使用 matlab.engine,它會在幕後驅動一個真正的 MATLAB 工作階段。eng = matlab.engine.start_matlab() 會啟動該工作階段,eng.load("sample.mat") 無論檔案版本為何都會把所有變數載入引擎工作區,eng.quit() 則在用完後將其關閉。回傳的值是 matlab.double 這類 MATLAB 型別,NumPy 可以用 np.asarray 進行轉換。

它的代價屬於實務層面而非格式層面:引擎需要已授權的 MATLAB 安裝以及相符的引擎版本,工作階段啟動需要數秒,而且無法在純 Python 環境中執行。這也是本節以內嵌方式展示呼叫、而非提供獨立範例的原因——只要機器上沒有 MATLAB,其他函式庫才是正確的預設選擇。