How to generate every possible permutation of a MATLAB structure - matlab

I have a MATLAB template structure that I'm using as a basis for a more complex output structure
conditions = {'Listen',':=:'};
items = ["apple" "duck"]; % 2 items
stims = cellstr([ ...
items, ... % text
items+".PNG", ... % picture
items+".wav" ... % sound
]);
event = struct( ...
'Cue', struct('duration',2,'jitter',0.25,'shows',conditions), ...
'Stimuli', struct('duration','sound','shows',stims), ...
'Delay', struct('duration',2,'jitter',0.25), ...
'Go', struct('duration',1,'jitter',0.25,'shows','Speak','skip','Cue==:=:'), ...
'Response', struct('duration',3,'jitter',0.25,'skip','Cue==:=:'));
events output is a 1×1 struct with the fields:
events =
1×1 struct with fields:
Cue: [1×2 struct]
Stimuli: [1×6 struct]
Delay: [1×1 struct]
Go: [1×1 struct]
Response: [1×1 struct]
I want to generate from this a 1×12 struct that contains each of the possible combinations of each top level field. Like below:
events =
1×12 struct array with fields:
Cue: [1×1 struct]
Stimuli: [1×1 struct]
Delay: [1×1 struct]
Go: [1×1 struct]
Response: [1×1 struct]
Edit: I should add that I want the output to dynamically reflect changes in the input. For example:
If the user decides to change event.Delay.duration = [1,2,3] it would generate a struct array three times the original size, with each possible delay duration implemented
If the user decides to remove the Cue field entirely, the output should be a struct array half the original size.

That should do the trick:
eC = struct2cell( event );
eN = fieldnames( event );
nums = cellfun( #numel, eC );
subs = cell( numel(nums), 1);
event = cell( prod(nums) , 1);
for k = 1 : prod(nums)
[ subs{:} ] = ind2sub( nums, k );
event{k} = cell2struct( cellfun(#(X,Y) X(Y), eC, subs, 'UniformOutput', false ), eN', 1 );
end
event = cat( 1, event{:} )';
Key point is the ind2sub function, which is in general helpful when dealing with multi-dimensional problems. Breaking them down to one dimension hast two advantages: 1) Nested loops are not needed. 2) We get independent from the number of dimensions.

Related

MATLAB: Access Data in Nested Structure using For loop

Good day,
I have a nested structure in this format.
Data: [1x1 struct]
a: [1x1 struct]
a1: [10x1 double]
a2: [10x1 double]
a3: [10x1 double]
b: [1x1 struct]
b1: [10x1 double]
b2: [10x1 double]
c: [1x1 struct]
c1: [10x1 double]
c2: [10x1 double]
c3: [10x1 double]
c4: [10x1 double]
Each of sub-fields of a, b & c are duration percentage of an event in buckets. The number of elements in each bucket are number of data-sets. I'd like sum the bucket values of each data set and discard the entire data-set if they don't add up to 100%. How may I access each element of the buckets for a, b & c fields of Data using for loop in a simply format.
EDIT: I figured out how to call the sub-fields & it's elements, sum the percentages & now, if the data-sets not adding up to 100 need to fully removed from each sub-field.
field = fieldnames(Data);
for group = 1:length(field)
for subfield = fieldnames(Data.(field{group}))
fieldSize = structfun(#(field) length(field),Data.(field{group}));
nb_datasets = fieldSize(1,1);
for jj = 1:nb_datasets
for ii = 1:length(subfield)
a_dataset_pcts(jj,ii) = Data.(field{group}).(subfield{ii})(jj,1);
end
a_pct_total(jj,:) = sum(a_dataset_pcts(jj,:));
end
end
end
Matlab like matrices Bensa.... You should drop your approach now.
Fixed size datasets
If all a, b, c arrays are 10x1 sized, you should switch immediately to a cell of arrays, being the index the durations, and the arrays columns the datasets for each duration:
a: [1x1 cell]
a{1}: [10xna double]
a{2}: [10xnb double]
a{3}: [10xnc double]
All statistics go easy. You have two for loops, one for the durations, other for the dataset. Matlab vectorizes the functions inside each dataset (most of the time). In here, i am assuming max(.) is your aggregation array function for obtaining statistics:
n=length(a);
for i=1:n
ni=size(a{i},2);
for j=1:ni
max(a{i})
mean(a{i}')'
if j==j0 && i==i0
a{i}(:,j)=[]; % Cleanup j0th event in i0th dataset
end
end
end
Do you need to compare a.a1 & b.b1 & c.c1? If that is true, then you forcedly should create the auxiliar array x, and then get your stats:
for i=1:n
x(:,i)=a{i};
end
max(x')'
Variable size datasets
If the a, b, c arrays are different each time, then you upgrade the last data and switch to a cell of cells:
a{1}: [1xna cell]
a{1,1}: [la(1)x1 double]
...
a{1,na}: [la(na)x1 double]
a{2}: [1xnb cell]
...
a{3}: [1xnc cell]
...
In here the max(.')' approach is useless, because every dataset could be of different length. If that is the case, you still can reference the data as usually through columns:
n=length(a);
for i=1:n
ni(i)=size(a{i},2);
for j=1:ni(i)
li=length(a{i,j}) % Length of each dataset
max(a{i,j})
% mean(a{i}')' % This do not work anymore
if j==j0 && i==i0
a{i,j}=[]; % Cleanup j0th event in i0th dataset
end
end
end
Do this make sense?. Feel free to comment below...

Iterating over Matlab struct

I have a 1x1 Matlab struct with 15 fields
results =
RAR_no_compartments_2_0: [1x1 struct]
RAR_no_compartments_2_1: [1x1 struct]
RAR_no_compartments_2_10: [1x1 struct]
RAR_no_compartments_2_11: [1x1 struct]
RAR_no_compartments_2_12: [1x1 struct]
RAR_no_compartments_2_13: [1x1 struct]
RAR_no_compartments_2_14: [1x1 struct]
RAR_no_compartments_2_2: [1x1 struct]
RAR_no_compartments_2_3: [1x1 struct]
RAR_no_compartments_2_4: [1x1 struct]
RAR_no_compartments_2_5: [1x1 struct]
RAR_no_compartments_2_6: [1x1 struct]
RAR_no_compartments_2_7: [1x1 struct]
RAR_no_compartments_2_8: [1x1 struct]
RAR_no_compartments_2_9: [1x1 struct]
I'm trying to cycle through each of these in a for loop:
model_names=fieldnames(results); %get the names of each sub-struct
fieldname_dims=size(fieldnames(results)); %get dimensions of each struct
for i=1:fieldname_dims(1), %iterate over number of substructs
name=model_names(i) %get the model name
results.(name) %access the substruct
end
However Matlab returns the following error:
Argument to dynamic structure reference must evaluate to a valid field name.
Could somebody please explain to me why this is an invalid field name?
This is precisely what structfun is made for. It allows you to loop through all the fields of a structure.
a = struct('a', 1, 'b', 2, 'c', 3);
structfun(#(x)disp(x), a)
1
2
3
Alternately, you may consider storing an array of structures rather than a structure where the fields have numbers in them like you have. Any time that you are reliant on a fieldname to know ordering, it may be better to use a different data structure.
For in your data, rather than having a fieldname of RAR_no_compartments_2_0 you'd simply assign the struct assigned to that field by index
RAR_no_compartments(1) = results.RAR_no_compartments_2_0;
RAR_no_compartments(2) = results.RAR_no_compartments_2_1;
...
This way, rather than a 1 x 1 struct with 15 fields, you'd have a 1 x 15 array of structs which is much easier to deal with natively within MATLAB.
Just a thought.
The problem is that fieldnames returns a cell array, so you have to use curly brackets to access the content of a cell. This should work:
model_names=fieldnames(results);
fieldname_dims=size(fieldnames(results),1);
for i=1:fieldname_dims
name=model_names{i};
results.(name)
end
The whole thing can be simplified by actually iterating over the cell array instead of using a counter and temporary variables:
for name = fieldnames(results).'
results.(name{:}) % do whatever with your field
end
However, the correct way to access all fields of a struct is structfun—see Suever's answer.

Using Cell Array Contents to Create Struct Entries

Say that I have two cell arrays, A and B, that contain string values. I wish to populate a struct S such I generate every possible combination of S.valueinA.valueinB = 1. I am currently trying to accomplish this with two nested for-loops that iterate through every possible combination and wanted to ask if there is a more efficient way to solve this problem in MATLAB.
If you want to make dynamic field names in structures, I don't see how else you can do it without two for loops. Let's say we have two cell arrays A and B that consist of string entries.
For my example, apologies for the strings inside these arrays in advance as I couldn't think of anything better at the moment!
Is this what you're trying to achieve?
S = struct();
A = {'hello', 'my', 'name', 'is', 'ray'};
B = {'i', 'am', 'doing', 'awesome'};
for idx = 1 : numel(A)
for idx2 = 1 : numel(B)
S.(A{idx}).(B{idx2}) = 1;
end
end
This creates a nested structure S such that for each element in A, this becomes a field in S where this field is another structure that contains fields with names coming from all elements in B.
If we displayed S, we get:
>> S
S =
hello: [1x1 struct]
my: [1x1 struct]
name: [1x1 struct]
is: [1x1 struct]
ray: [1x1 struct]
If we accessed the hello field of S, we get:
>> S.hello
ans =
i: 1
am: 1
doing: 1
awesome: 1
Similarly, if we accessed the my field, we get:
>> S.my
ans =
i: 1
am: 1
doing: 1
awesome: 1
Therefore, if we want to get the hello field followed by the am field, we do:
>> S.hello.am
ans =
1

Removing Structures

I am programming a GUI and I have a variable structure like:
bin: 0.2000
Logical1: 0
Logical2: 0
Matrixraw: {[1x1 struct] [1x1 struct]}
Matrixfiltered: {[1x1 struct] [1x1 struct]}
Matrixcorrected: {[1x1 struct] [1x1 struct]}
Every input file has its own matrix. In this case, we have two input files because every Matrix has two sructures. If we put 3 input files would be 3 structures and so on.
I want to make a button that given one index 1,2,3.. erases the i'th component from the Matrix structure. So, if you type 2 it will erase the second component of the matrix and would give:
bin: 0.2000
Logical1: 0
Logical2: 0
Matrixraw: {[1x1 struct]}
Matrixfiltered: {[1x1 struct]}
Matrixcorrected: {[1x1 struct]}
Thanks.
If you want to delete a member of structure:
Matrixraw = struct('field1', 1, 'field2', '2')
Matrixraw =
field1: 1
field2: 2
// delete first element of structure
Matrixraw(1) = []
Matrixraw =
field1: []
field2: []
The syntax below will empty the structure and remove completely your variable data:
// both works
Matrixraw = {}
Matrixraw = []
Here is a simple way to do it.
Suppose you have this:
Matrixfiltered= {[1 2], {}, 4}
and now we want to remove the second one:
Matrixfiltered(2) = []
Now you have this:
Matrixfiltered= {[1 2], 4}
Just give it a try!

Find Values in cell array of struct

I have two cell array's with structure in it.
example:
xmlFB =
Columns 1 through 5
[1x1 struct] [1x1 struct] [1x1 struct] [1x1 struct] [1x1 struct]
xmllink =
Columns 1 through 3
[1x1 struct] [1x1 struct] [1x1 struct]
xmlFB{1}
ans =
Param: {[1x1 struct] [1x1 struct]}
InterChartConnection: [1x1 struct]
Tasks: [1x1 struct]
Attributes: [1x1 struct]
xmllink{1}
ans =
Attributes: [1x1 struct]
In "xmllink" there is the struct "Attributes" and in there is the Field "Name" with the Value "EN1"
And in xmlFB in the struct "Attributes" there are two Fields "Name" and "Typ"
The Names are unique.
What i want to do is to find the "Typ" in "xmlFB" from the names in the "xmllink".
First i wanted to do with a Loop, but the i read about these arrayfun/structfun/cellfun functions from Matlab.
Is there a way to do this with these? Or is a Loop better?
Let's assume that all variables in Names and Typ are strings. (Should work for scalars as well.)
Here is how to get the values into cell arrays and link them.
% create cell arrays
xmllinkname = cellfun(#(x) x.Attributes.Name, xmllink, 'UniformOutput',0);
xmlFBname = cellfun(#(x) x.Attributes.Name, xmlFB, 'UniformOutput',0);
xmlFBtyp = cellfun(#(x) x.Attributes.Typ, xmlFB, 'UniformOutput',0);
% match names
[idx1, idx2] = ismember(xmllinkname,xmlFBname);
idx2(idx2==0)=[]; % in case some names in xmllink absent in xmlFB
% get matched names and typ
xmllinknamematched = xmllinkname(idx1);
xmllinktyp = xmlFBtyp(idx2);
Since the order of values in the first cell arrays will be the same as in your original cell arrays of structures, you can use those indices idx1 and idx2 to match the structures as well.
xmllinkmatched = xmllink(idx1);
xmlFBmatched = xmlFB(idx2);
Of course, you can avoid creating the temporary arrays and put the first two cellfun statement into ismember statement.
Use the loop.
First things first: only start worrying about performance when it is actually a problem, and only after you get it right.
Now, if I understood you correctly, here are generally two ways to accomplish what you want:
% create some bogus data with the same structure
% ---------------------------
f_xmllink = #(N) struct(...
'Attributes', struct(...
'Name', num2str(N))...
);
f_xmlFB = #(N) struct(...
'Attributes', struct(...
'Name', num2str(N),...
'Typ' , num2str(N))...
);
% using numbers as names
xmllink = {
f_xmllink(190)
f_xmllink(331) % 2
f_xmllink(321) % 3
f_xmllink(239)
};
xmlFB = {
f_xmlFB(331) % 1
f_xmlFB(200)
f_xmlFB(108)
f_xmlFB(321) % 4
f_xmlFB(035)
};
% Example of a no-loop approach
% ---------------------------
tic
s_exp = #(s, field) [s.(field)];
s_exp_C = #(s, field) {s.(field)};
one = s_exp_C(s_exp( [xmllink{:}], 'Attributes'), 'Name');
two = s_exp_C(s_exp( [xmlFB{:}], 'Attributes'), 'Name');
[i,j] = find(strcmp(repmat(one,numel(two),1), repmat(two,numel(one),1).'));
s_exp_C(s_exp([xmlFB{i}], 'Attributes'), 'Typ')
toc
% Example of a loop approach
% ---------------------------
tic
for ii = 1:numel(xmllink)
S = [xmlFB{:}];
S = [S.Attributes];
S = {S.Name};
ind = strmatch(xmllink{ii}.Attributes.Name, S);
if ~isempty(ind)
xmlFB{ind}.Attributes.Typ
end
end
toc
Output:
% no-loop
ans =
'331' '321' % correct findings
Elapsed time is 0.001103 seconds.
% loop
ans =
331 % correct findings
ans =
321
Elapsed time is 0.000666 seconds. % FASTER!
Granted, it's not a fair test to compare the performance, but I think everyone will agree that the looped version will at the very least not be slower than the non-loop version.
More importantly, speed isn't everything -- how long did it take you to understand the no-loop solution? Chances are you understood the loop solution in one read, whereas the no-loop solution is a lot more complicated and will have to be thoroughly documented and tested etc. Also, changes to the no-loop solution will be harder to implement than changes in the loop solution.
That advice to not use loops in Matlab is completely outdated; please ignore it :)