0

I am trying to create a Matlab file (*.mat) from Python that contains a Matlab data structure that would look like:

s.key1 where key1 is an array of values
s.key2 where key2 is an array of 1D arrays 
s.key3 where key3 is an array of 2D arrays 

If I use savemat and a dictionary, the Matlab output is a cell array rather than a Matlab data structure.

I have tried using

np.core.records.fromarrays(data_list, names=q_keys)

but this doesn't seem to work for keys with 2D arrays. I have both 2D and 3D arrays that need to be in a Matlab structure for compatibility with an existing file format. Is there a way to do this in Python?

Thanks

0

2 Answers 2

1

Here's a stab at the task:

In [292]: dt = np.dtype([('key1',int),('key2',int, (3,)),('key3',object)])
In [293]: arr = np.zeros((5,), dt)
In [294]: arr
Out[294]: 
array([(0, [0, 0, 0], 0), (0, [0, 0, 0], 0), (0, [0, 0, 0], 0),
       (0, [0, 0, 0], 0), (0, [0, 0, 0], 0)],
      dtype=[('key1', '<i8'), ('key2', '<i8', (3,)), ('key3', 'O')])
In [295]: arr['key1']=np.arange(5)
In [296]: arr['key2']=np.arange(15).reshape(5,3)
In [302]: arr['key3']=[1,np.arange(5),np.ones((2,3),int),'astring',[['a','b']]]
In [303]: io.savemat('test.mat', {'astruct':arr})

In Octave:

>> load test.mat
>> format compact
>> astruct
astruct =

  1x5 struct array containing the fields:

    key1
    key2
    key3
>> astruc.key1
error: 'astruc' undefined near line 1 column 1
>> astruct.key1
ans = 0
ans = 1
ans = 2
ans = 3
ans = 4
>> astruct.key2
ans =
  0  1  2
ans =
  3  4  5
ans =
  6  7  8
ans =
   9  10  11
ans =
  12  13  14
>> astruct.key3
ans = 1
ans =
  0  1  2  3  4
ans =
  1  1  1
  1  1  1
ans = astring
ans = ab

Back in ipython:

In [304]: d = io.loadmat('test.mat')
In [305]: d
Out[305]: 
{'__header__': b'MATLAB 5.0 MAT-file Platform: posix, Created on: Wed Jun  6 15:36:23 2018',
 '__version__': '1.0',
 '__globals__': [],
 'astruct': array([[(array([[0]]), array([[0, 1, 2]]), array([[1]])),
         (array([[1]]), array([[3, 4, 5]]), array([[0, 1, 2, 3, 4]])),
         (array([[2]]), array([[6, 7, 8]]), array([[1, 1, 1],
        [1, 1, 1]])),
         (array([[3]]), array([[ 9, 10, 11]]), array(['astring'], dtype='<U7')),
         (array([[4]]), array([[12, 13, 14]]), array([['a', 'b']], dtype='<U1'))]],
       dtype=[('key1', 'O'), ('key2', 'O'), ('key3', 'O')])}

So while a created a numpy structured array with dtypes like int and int(3), the loaded array has object dtype for all fields. loadmat makes heavy use of object dtype arrays to handle the generality of MATLAB cells and struct. loadmat has various loading parameters, which we can play with.

This was just a guess based on previous experience loading MATLAB files. If this isn't what you want, I'd suggest constructing sample data in MATLAB, save that, and then load to see how loadmat constructs it. You may have to go back and forth a few times to work out the bugs.

Sign up to request clarification or add additional context in comments.

Comments

0

Given the direction provided by hpaulj, I developed the following function that created a structure from a list of objects.

    def listobj2struct(list_in):
    """Converts a list of objects to a structured array.

    Parameters
    ----------
    list_in: list
        List of objects

    Returns
    -------
    struct: np.array
        Structured array
    """

    # Create data type for each variable in object
    keys = list(vars(list_in[0]).keys())
    data_type = []
    for key in keys:
        data_type.append((key, list))

    # Create structured array based on data type and length of list
    dt = np.dtype(data_type)
    struct = np.zeros((len(list_in),), dt)

    # Populate the structure with data from the objects
    for n, item in enumerate(list_in):
        new_dict = vars(item)
        for key in new_dict:
            struct[key][n] = new_dict[key]

    return struct

To complete what I needed to do to create a Matlab file from a complex nesting of objects I also wrote the following functions. Perhaps this will help others facing similar tasks. There may be better ways, but this worked for me.

    def obj2dict(obj):
    """Converts object variables to dictionaries. Works recursively to all levels of objects.

    Parameters
    ----------
    obj: object
        Object of some class

    Returns
    -------
    obj_dict: dict
        Dictionary of all object variables
    """

    obj_dict = vars(obj)
    for key in obj_dict:
        # Clean out NoneTypes
        if obj_dict[key] is None:
            obj_dict[key] = []
        # If variable is another object convert to dictionary recursively
        elif str(type(obj_dict[key]))[8:13] == 'Class':
            obj_dict[key]=obj2dict(obj_dict[key])

    return obj_dict


def listobj2dict(list_in):
    """Converts list of objects to list of dictionaries. Works recursively to all levels of objects.

    Parameters
    ----------
    obj: object
        Object of some class

    Returns
    -------
    new_list: list
        List of dictionaries
    """
    new_list = []
    for obj in list_in:
        new_list.append(obj2dict(obj))
    return new_list


def listdict2struct(list_in):
    """Converts a list of dictionaries to a structured array.

    Parameters
    ----------
    list_in: list
        List of dictionaries

    Returns
    -------
    struct: np.array
        Structured array
    """

    # Create data type for each variable in object
    keys = list(list_in[0].keys())
    data_type = []
    for key in keys:
        data_type.append((key, list))

    # Create structured array based on data type and length of list
    dt = np.dtype(data_type)
    struct = np.zeros((len(list_in),), dt)

    # Populate the structure with data from the objects
    for n, item in enumerate(list_in):
        new_dict = item
        for key in new_dict:
            struct[key][n] = new_dict[key]

    return struct

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.