12

Consider the following Matlab code:

pmod(1).name{1}  = 'regressor1';
pmod(1).param{1} = [1 2 4 5 6];
pmod(1).poly{1}  = 1; 
pmod(2).name{1}  = 'regressor2-1';
pmod(2).param{1} = [1 3 5 7]; 
pmod(2).poly{1}  = 1;

This creates a struct array. Each struct in the array contains three fields of type cell. As such, we have the following hierarchy in pmod:

pmod  // struct array
|
*- struct
|  |
|  *- cell  // contains 1 or more strings
|  *- cell  // contains 1 or more arrays
|  *- cell  // contains 1 or more arrays
|
*- struct [...]

I'm trying to use scipy.io to produce the above data structures in Python, such that they can be loaded into Matlab (this hierarchy is required by SPM).

Creating a struct is straightforward, as scipy.io.savemat saves any dict whose keys are all of type str as a Matlab struct:

from scipy.io import savemat

struct = {
    'field1': 1,
    'field2': 2,
}

savemat('/tmp/p.mat', {'a_struct': struct})

However, when trying to generalize this to a struct array, I hit the following roadblock:

struct_array = [struct, struct]
savemat('/tmp/p.mat', {'s_array': struct_array})

This does not behave as expected; when loading p.mat into Matlab, I get a 1x2 cell array, not a struct array.

How can I create a struct array using scipy.io?

Notes:

  1. I have tried savemat('/tmp/p.mat', np.array(struct_array)) and savemat('/tmp/p.mat', np.array(struct_array, dtype=object)), to no avail.
Community
  • 1
  • 1
Louis Thibault
  • 20,240
  • 25
  • 83
  • 152
  • found this post from a search about struct array to deal with the exact same problem in SPM. Thanks for posting! – Ben Smith May 24 '22 at 16:32

1 Answers1

14

You can use np.core.records.fromarrays to construct a record array, which is roughly equivalent to a MATLAB struct, and will be converted to a MATLAB struct by scip.io.savemat.

from numpy.core.records import fromarrays
from scipy.io import savemat

myrec = fromarrays([[1, 10], [2, 20]], names=['field1', 'field2'])
savemat('p.mat', {'myrec': myrec})

When opened in MATLAB, this gives:

>> load('p.mat')
>> myrec

myrec = 

1x2 struct array with fields:

    field1
    field2
TheBlackCat
  • 9,791
  • 3
  • 24
  • 31
  • I can create an array struct using this code, but I am not sure how to create an array of structs. I tried creating the structures first and then the array, but I have a dictionary with fields that change in size, so fromarrays does not work. – Ivan Felipe Rodríguez Feb 08 '19 at 12:53
  • @IvanFelipeRodríguez did you try creating an object array where each element is a record array? You can create each element using the above method and then assign them to the object array. – TheBlackCat Feb 08 '19 at 13:20
  • hey @TheBlackCat. thank you for taking a look on this. I just tried initializing first every element as a record but when I run fromarrays... it raises this error " Empty data-type" – Ivan Felipe Rodríguez Feb 08 '19 at 18:01
  • I posted my question [here](https://stackoverflow.com/questions/54598286/how-to-save-a-list-of-python-dictionaries-as-an-array-of-matlab-structured-array) – Ivan Felipe Rodríguez Feb 08 '19 at 18:29
  • 1
    This works for me except in a case like this: `myrec = fromarrays([[1, 10], [[2,3], [20, 22]]], names=['field1', 'field2'])` . I want to use this to store an array of structs where some fields of the structs are matlab arrays. I get `ValueError: array-shape mismatch`. How do I store an array as a struct field? – 3bdalla Jul 30 '19 at 11:24