Search code examples
matlabstructureoperation

Calculate means of fields with same name in cell of structures


I have a cell containing a number of structures, containing the output from the MirToolBox mirfeatures() function for a number of audio signals. The field names are the same for each structure.

So the data looks something like this, but much larger. c{1} is the first structure, c{2} is the second structure etc. Both structures contain the same field names.

c{1}.field1.field1A=1;
c{1}.field1.field1B=2;
c{1}.field1.field1C=3;
c{1}.field2.field2D.field2E=4;
c{1}.field2.field2D.field2F=5;
c{1}.field3=6;
c{1}.field4.field4A.field4B.field4C=7;
c{1}.field4.field4A.field4B.field4D=8;
c{2}.field1.field1A=9;
c{2}.field1.field1B=10;
c{2}.field1.field1C=11;
c{2}.field2.field2D.field2E=12;
c{2}.field2.field2D.field2F=13;
c{2}.field3=14;
c{2}.field4.field4A.field4B.field4C=15;
c{2}.field4.field4A.field4B.field4D=16;

I'd like to calculate the mean values of each field, across structures e.g.

The mean of c{1}.field1.field1A, c{2}.field1.field1A,.. c{n}.field1.field1A=9.

Is there a straightforward way of doing this, bearing in mind the actual structures that I'm working with have approaching a hundred fields each?

Thanks


Solution

  • You can use cell2mat to convert your cell array to a struct array, and then access a field over the whole array (as opposed to simply from a single struct element from that array) to get a comma-separated list. If the field points to another struct, your comma separated list will be a comma separated list of structs!

    Then you can collect that comma-separated list into a cell array again, by surrounding it with {}, and performing a cell2mat again, and so forth for each level in your struct, to obtain struct arrays of the same size as your initial cell array, containing only the fields you need.

    When you've finally travelled enough levels down to reach the field of interest, you can collect it again as a comma separated list, and wrap it around [] to make it into a matrix, which you can use as an argument to your function (e.g. mean).

    Unfortunately, you'll have to do this for each unique innermost field you have, because there is no consistency between them. But it does allow you to process all N cells in a single operation for each unique field.

    e.g. given your above cell of size 2, and if we denote s0, s1, s2 as outermost, one level deep, two levels deep, etc, then:

    % Convert outer cell array to struct array representing outermost level
    s0 = cell2mat(c); % -> 1x2 struct array containing the fields: field1, field2, field3, field4
    
    % field 1 - subfields of interest are 1 level deep
    s1 = cell2mat({s0.field1}); % -> 1x2 struct array containing the fields: field1A, field1B, field1C
    field1A_mean = mean([s1.field1A])
    field1B_mean = mean([s1.field1B])
    field1C_mean = mean([s1.field1C])
    
    % field 2 - subfields of interest are 2 levels deep
    s1 = cell2mat({s0.field2}); % -> 1x2 struct array containing the field: field2D
    s2 = cell2mat({s1.field2D}); % -> 1x2 struct array containing the fields: field2E, field2F 
    field2E_mean = mean([s2.field2E])
    field2F_mean = mean([s2.field2F])
    
    % field 3 - collect at this level
    field3_mean = mean([s0.field3])
    
    % field 4 subfields of interest are 3 levels deep
    s1 = cell2mat({s0.field4});
    s2 = cell2mat({s1.field4A});
    s3 = cell2mat({s2.field4B});
    field4C_mean = mean([s3.field4C])
    field4D_mean = mean([s3.field4D])