Convert MAT file with array of nested structs into Python

69 views Asked by At

I would like to convert a complex MAT file that include numbers, structs, arrays and array of nested structs, in which those structs could have their "mother" contents types, into a Python dictionary of a same hierarchical shape.

Found a converter for similar case on this link which originally written for Python 2.7. However, this converter does not support array of structs.

A representative sample MAT file could be created using the follow Matlab script:

item11 = 11;
item12 = '12';
item21 = 21;
item22 = '22';
struct1.item1 = item11;
struct1.item2 = item12;
struct2.item1 = item21;
struct2.item2 = item22;

ar = [struct1, struct2];
1

There are 1 answers

0
David Cohen On

By altering the similar case on this example and adding a check whether an array contains Matlab object scipy.io.matlab._mio5_params.mat_struct, and if it does, recursively convert its contents and save it into a list object. The solution can support 1D array of structs.

Conversion functions:

import scipy
import numpy as np

def _check_keys(dict):
    '''
    checks if entries in dictionary are mat-objects. If yes
    todict is called to change them to nested dictionaries
    '''
    for key in dict:
        if isinstance(dict[key], scipy.io.matlab._mio5_params.mat_struct):
            dict[key] = _todict(dict[key])
        elif isinstance(dict[key], np.ndarray): # assuming 1D array
            elem = dict[key]
            if elem.shape[0]:
                if isinstance(elem[0], scipy.io.matlab._mio5_params.mat_struct): # assuming 1D array of matlab structs 
                    list_structs = [] # instead array create python list includes the nested structs as dictionaries
                    for idx_array in range(elem.shape[0]): 
                        list_structs.append(_todict(elem[idx_array]))
                    dict[key] = list_structs # save as list of dictionaries

    return dict         

def _todict(matobj):
    '''
    A recursive function which constructs from matobjects nested dictionaries
    '''
    dict = {}
    for strg in matobj._fieldnames:
        elem = matobj.__dict__[strg]
        if isinstance(elem, scipy.io.matlab._mio5_params.mat_struct):
            dict[strg] = _todict(elem)
        elif isinstance(elem, np.ndarray): # assuming 1D array
            if elem.shape[0]:
                if isinstance(elem[0], scipy.io.matlab._mio5_params.mat_struct): # assuming 1D array of matlab structs 
                    list_structs = [] # instead array create python list includes the nested structs as dictionaries
                    for idx_array in range(elem.shape[0]): 
                        list_structs.append(_todict(elem[idx_array]))
                    dict[strg] = list_structs # save as list of dictionaries
                else:
                    dict[strg] = elem
            else:
                dict[strg] = elem
        else:
            dict[strg] = elem
    return dict

M = scipy.io.loadmat('Sample.mat', struct_as_record=False, squeeze_me=True)
mdict = _check_keys(M)
mdict

The above sample resulting after conversion:

{'__header__': b'MATLAB 5.0 MAT-file, Platform: MACI64, Created on: Mon Nov 20 10:05:34 2023',
 '__version__': '1.0',
 '__globals__': [],
 'ar': [{'item1': 11, 'item2': '12'}, {'item1': 21, 'item2': '22'}],
 'item11': 11,
 'item12': '12',
 'item21': 21,
 'item22': '22',
 'struct1': {'item1': 11, 'item2': '12'},
 'struct2': {'item1': 21, 'item2': '22'}}