Reputation: 4240
I have a function that processes an individual data set, and stores the outcome metrics in a data set like so:
trial_data.output_metric_a.value_1 = 2;
...
trial_1 = trial_data;
This function is applied to a number of different trials and are stored as an array struct:
% trial_1.output_metric_a.value_1 = 4
% trial_2.output_metric_a.value_1 = 2
trials = [trial_1 ; trial_2];
Is it possible to get averages and standard deviations of the sub-field values without looping through the data structure?
Ideally:
mean_trials = mean(trials)
mean_trials.output_metric_a.value_1 == 3 % true
A possible loop implementation to solve this could be (obviously this leaves much to be desired):
output_metric_a_value_1 = [];
...
for i:length(trials)
output_metric_a_value_1(end+1) = trials(i).output_metric_a.value_1;
... % For each output metric and value
end
mean_trials.output_metric_a.value_1 = mean(output_metric_a_value_1);
Upvotes: 3
Views: 450
Reputation: 2180
Special Solution (not-nested struct > array)
As already mentioned, aiming for just one level of struct fields (not nested) one can basically go for a one-liner:
sarr_mean = cellfun(@(fn) mean([sarr.(fn)]), fieldnames(sarr))
Remark: In the not-nested case, there is not really a need to assign the resulting array back to a struct. If required, you can do it analogously to the full solution below.
Full Solution (nested struct > nested struct)
However, with arbitrarily nested arrays, I suggest to use a function such as the following:
% f... function handle
% s... nested struct array
function sarr_res = nestedSarrFun(f, s)
if isstruct(s)
% get fieldnames:
fns = fieldnames(s);
% get content:
con = cellfun(@(fn) nestedSarrFun(f, [s.(fn)]), ...
fns, 'UniformOutput', false);
% create return struct
fnsCon = reshape([fns(:), con(:)]', [1,2*numel(fns)]);
sarr_res = struct(fnsCon{:});
else
sarr_res = f(s);
end
end
Usage Example
Define example struct array and apply mean
via nestedSarrFun
:
% define example struct array "sarr" with fields
% .foo.bar1
% .bar2
% .dings
sarr = struct();
sarr(1).foo.bar1 = 2;
sarr(1).foo.bar2 = 7;
sarr(1).dings = 1;
sarr(2).foo.bar1 = 5;
sarr(2).foo.bar2 = 5;
sarr(2).dings = 2;
% apply mean to all nested fields:
sarr_mean = nestedSarrFun(@mean, sarr);
Example Result:
sarr_mean.foo.bar1 = 3.5
sarr_mean.foo.bar2 = 6
sarr_mean.dings = 1.5
Upvotes: 2
Reputation: 1196
In Matlab2017 (I am not sure about older versions), an array of structs can return an array of the field of its structs like so:
struct1.x = 1;
struct2.x = 2;
% array of 2 structs:
struct_array = [struct1, struct2];
% array of field x of each struct:
[struct_array.x]
which returns
ans =
1 2
In your case, the data is not in a field of your struct, but in a subfield output_metric_a
. Therefore, you first need to this twice:
trial1.output_metric_a.value_1 = 1;
trial2.output_metric_a.value_1 = 2;
trials = [trial1, trial2];
output_metric_a_array = [trials.output_metric_a];
value_1_array = [output_metric_a_array.value_1];
mean_of_value_1 = mean(value_1_array)
which returns
mean_of_value_1 =
1.5000
Upvotes: 1
Reputation: 3071
You can convert the main struct to cell, then operate on the contents:
% your data base:
trial_1.output_metric_a.value_1 = 4
trial_2.output_metric_a.value_1 = 2
trials = [trial_1 ; trial_2];
% convert to cell:
Ctrials=struct2cell(trials);
Atrials=[Ctrials{:}];
meanTrials=mean([Atrials.value_1])
meanTrials=
3
Upvotes: 4