Select specific data from all fields in structure
15 次查看(过去 30 天)
显示 更早的评论
Hi all,
I have a data structure (data_freq) where all the data of my subjects is stored:
data_freq =
p28: [1x1 struct]
p29: [1x1 struct]
p30: [1x1 struct]
One field contains the sessions 'OFF' and 'ON': data_freq.p28
ans =
OFF: [1x1 struct]
ON: [1x1 struct]
And within the field 'OFF' or 'ON' there are the different conditions: data_freq.p28.OFF
ans =
Rest1: [1x1 struct]
Rest2: [1x1 struct]
Rest3: [1x1 struct]
RestCOG1: [1x1 struct]
RestCOG2: [1x1 struct]
RestCOG3: [1x1 struct]
And within these condition fields there is a field containing a matrix which represents my data. My question: I would like to select all these matrices for a specific subset of subjects/sessions/conditions. For example, I would like to select the fields of all subjects, the session 'OFF' and condition 'Rest1'. I'm not really familiar with this type of selection (or if its even possible in a simple way). I usually store my data in a matrix where every columns encodes the session/condition, after which I select it in the following way: select = data_freq(:,2)==sessioncode & data_freq(:,3)==conditioncode; data_freq = data_freq(select,:);
Does anyone know if you can do something similar like this for structures? I know about dynamic fields so I can use that if I need one specific field, but how do I select multiple fields (i.e. for all subjects, or if I would want two conditions)? I guess it would have to look something like this:
mtx = data_freq.(all).('OFF').('Rest1').data mtx = {3x1 cell}
Where one cell contains an [MxN double] (so select the data for all three subjets with session OFF and condition Rest1). The reason I want this is that after this step I can easily calculate averages/std of my data.
0 个评论
回答(2 个)
Guillaume
2015-6-16
编辑:Guillaume
2015-6-16
Multilevel structure arrays are difficult to manipulate in matlab so I tend to avoid them. In your case it's not so much of a problem as all your structures are scalar.
An alternative to eval often suggested on this forum is to use structures with dynamic field names, as you've done for your p28 to p30 fields. I think it is just as bad a practice as eval as you still end up with unknown variable names. A structure array would make more sense, but see point 1.
Anyway with your example, the simplest way to access the data is to use structfun to iterate over the first level, and plain dot notation for the remaining. if the final level data is not scalar, then you have to specify 'UniformOutput', false to structfun. In that case, you can then convert the output structure into a cell array with struct2cell:
%build a demo / testing structure:
node0 = @() struct('data', rand(5, 3));
node1 = @() struct('Rest1', node0(), ...
'Rest2', node0(), ...
'Rest3', node0(), ...
'RestCOG1', node0(), ...
'RestCOG2', node0(), ...
'RestCOG3', node0());
node2 = @() struct('OFF', node1(), 'ON', node1());
node3 = @() struct('p28', node2(), 'p29', node2(), 'p30', node2());
data_freq = node3();
%access data_freq.(all).('OFF').('Rest1').data
mtx = struct2cell(structfun(@(px) px.OFF.Rest1.data, data_freq, 'UniformOutput', false))
2 个评论
Guillaume
2015-6-16
The following function will allow you to do this:
function out = selectfields(s, fields, scalarfields)
%selectfields return the fields of a structure array that matches the given list of field names
%
%syntax:
% out = selectfields(s, fields)
% out = selectfields(s, fields, asarray)
%
%with:
% s: a structure
% fields: a cell array of field names
% scalarfields: logical indicating whether the returned fields are scalar (true, default) or not
% out: a column vector of field values (if scalarfields is true or omitted), a column cell array otherwise
%
%author: G. de Sercey
validateattributes(s, {'struct'}, {});
validateattributes(fields, {'cell'}, {});
if nargin < 3
scalarfields = true;
else
validateattributes(scalarfields, {'logical'}, {'scalar'});
end
if isrow(fields)
fields = fields';
end
out = arrayfun(@(ss) cellfun(@(f) ss.(f), fields, 'UniformOutput', scalarfields), s, 'UniformOutput', false);
out = vertcat(out{:});
end
You can then select the output you want by chaining calls to selectfields:
selection = selectfields(data_freq, {'p28', 'p30'});
selection = selectfields(selection, {'ON'});
selection = selectfields(selection, {'Rest1', 'Rest2', 'RestCOG2'});
mtx = selectfields(selection, {'data'}, false)
Note that the last line could also be:
mtx = {selection.data}'
Peter Perkins
2015-6-16
编辑:Peter Perkins
2015-6-16
Michiel, this doesn't answer your question directly, but may be of some help if you have a version of MATLAB from R2013b on.
You don't say why your data are organized like that. There's nothing wrong with that format per se, but it makes the kind of subselection you want difficult. It may be the format expected by some other code, or some other external reason. But you might find it simpler to put the data into a table, in much the same way you have described as, "store my data in a matrix".
For example, first cook up some fake data as you describe:
subjects = {'p28' 'p29' 'p30'}';
sessions = {'ON' 'OFF'}';
conditions = {'Rest1' 'Rest2' 'Rest2' 'RestCOG1' 'RestCOG2' 'RestCOG2'}';
for i1 = 1:length(subjects)
subject = subjects{i1};
for i2 = 1:length(sessions)
session = sessions{i2};
for i3 = 1:length(conditions)
condition = conditions{i3};
data_freq.(subject).(session).(condition) = struct('a',randn,'b',randn);
end
end
end
Now convert to a table. There are probably many ways to do this, here's one:
nrows = length(subjects)*length(sessions)*length(conditions);
Subject = categorical( repelem(subjects,length(sessions)*length(conditions),1) );
Session = categorical( repmat(repelem(sessions,length(conditions),1),length(subjects),1) );
Condition = categorical( repmat(conditions,length(subjects)*length(sessions),1) );
A = NaN(nrows,1);
B = NaN(nrows,1);
df = table(Subject,Session,Condition,A,B);
j = 0;
for i1 = 1:length(subjects)
subject = subjects{i1};
for i2 = 1:length(sessions)
session = sessions{i2};
for i3 = 1:length(conditions)
condition = conditions{i3};
j = j + 1;
df(j,{'A' 'B'}) = struct2table(data_freq.(subject).(session).(condition));
end
end
end
OK, what to do with this? Here are three versions of "select the fields of all subjects, the session 'OFF' and condition 'Rest1'":
>> offRest1 = df.Session=='OFF' & df.Condition=='Rest1';
>> df(offRest1,:)
ans =
Subject Session Condition A B
_______ _______ _________ _______ ________
p28 OFF Rest1 -1.6258 -1.9648
p29 OFF Rest1 0.43638 -0.50436
p30 OFF Rest1 0.49016 0.76525
>> df{offRest1,{'A' 'B'}}
ans =
-1.6258 -1.9648
0.43638 -0.50436
0.49016 0.76525
>> df.A(offRest1)
ans =
-1.6258
0.43638
0.49016
2 个评论
Guillaume
2015-6-17
Hum! You must have missed my reply to your comment. The function to do the selection is there.
另请参阅
类别
在 Help Center 和 File Exchange 中查找有关 Cell Arrays 的更多信息
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!