Using Cell Array Contents to Create Struct Entries - matlab

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

Related

MATLAB: Storing values in a structure

Good day MATLAB pros,
I have a long list of (single value) variables in my workspace that has to be stored into an array for every loop of execution.
Here's a simple example:
Variables in the workspace:
a = 1;
b = 2.2;
c = 3.4;
d = [0.5 0.7 1 1.2 1.5];
e = [-15 -10 -5 0 5 10 15 20];
serial = {'WTZ00151'};
model = {'336F'};
NameList = {'a';'serial';'model'};
1) Here, I'm saving only the single value variables into Data structure, however what I'd like to do is for every loop, save the single values into an array in Data structure.
varList = who;
Data = struct;
fields = fieldnames(Data, '-full');
fieldSizes = structfun(#(field) length(field),Data);
% removing arrays from structure
for lst = 1:length(fieldSizes)
if fieldSizes(lst) > 1
Data = rmfield(Data,fields(lst));
end
end
Data =
Data: [1x1 struct]
a: 1
b: 2.2000
c: 3.4000
index: 10
model: {'336F'}
serial: {'WTZ00151'}
So if I run this in a loop, for i = 1:5, Data should look like this:
Data =
Data: [1x1 struct]
a: [1 1 1 1 1]
b: [1x5 double]
c: [1x5 double]
index: [10 10 10 10 10]
model: {1x5 cell}
serial: {1x5 cell}
Any ideas on how to code the for loop?
2) Since there are too many variables in the workspace & I have a long list of variables that needs storing, instead of using who to save ALL variables to the structure (and then filtering out the unwanted), how could I use a list of variable names (imported from a text file: NameList) to call out what needs to be stored? Using the variable name from NameList does not call out the structure values.
Much appreciated,
It's not immediately clear what part of your code actually is creating your data structure. There are several ways to create a struct from your array of variable names.
One way is to save the relevant variables to a file and load them back into a struct
save('tmp.mat', NameList{:});
Data = load('tmp.mat');
Another option (not recommended) is to use eval
for k = 1:numel(NameList)
Data.(NameList{k}) = eval(NameList{k});
end
As far as storing data from multiple iterations, I personally would recommend storing the data into an array of struct rather than a struct of arrays. You should be able to store each Data instance in an array using k as an index as shown below:
allData(k) = Data;
If you decide you really want a struct of arrays, you can always convert it afterwards.
fields = fieldnames(allData);
output = struct();
for k = 1:numel(fields)
% Try to just concatenate the values together
try
values = [allData.(fields{k})];
% If that failed, concatenate them within a cell array
catch
values = {allData.(fields{k})};
end
% Store this in a single output struct
output.(fields{k}) = values;
end

How do I remove the elements of one vector from another?

I have a double value, A, which is
[1,4,7,6]
I also have B, which is an array that contains many more values. I have a new variable, C, which is essentially a double value of all these numbers (all of them in one cell, vs. five).
[1,4,7,6]
[2,6,9,12]
[3,1,17,13]
[5,7,13,19]
[1,5,9,15]
How do I remove the elements (not the actual values) from C? I want to end up with this.
[2,6,9,12,3,1,17,13,5,7,13,19,1,5,9,15]
How do I get this? I've used these commands:
C(A) = [];
and
C = C(setdiff(1:length(C),A));
The problem is that when I run that command, I get this instead of what I want.
[4,7,2,12,3,1,17,13,5,7,13,19,1,5,9,15]
Clearly that isn't the same as what I have. It's throwing off the rest of my results and I need to fix this specific issue.
Thanks in advance :)
EDIT:
So I figured out that it's spewing the CORRECT numbers out, just in the wrong order. I have to sort it in order for it to work correctly. This is a problem because it causes the next command to be non-functional because the ismember command has issues with the removal (I don't know why, I'm still working on it).
Double array case
If B is a double array, you can use setdiff with 'rows' and 'stable' options, like so -
C = reshape(setdiff(B,A,'rows','stable').',1,[])
With ismember, you can perform the same operation, like so -
C = reshape(B(~ismember(B,A,'rows'),:).',1,[])
You can also use a bsxfun approach as suggested by #Amro -
C = reshape(B(~all(bsxfun(#eq, B, A),2),:).',1,[])
Cell array case
If B is a cell array with number of elements in each cell equal to the number of elements in A, then you can firstly convert it to a double array - B = vertcat(B{:}) and then use either of the above mentioned tools.
Or you can use a cellfun based approach that avoids conversion to a double array, like so -
excl_rows = B(~cellfun(#(x1,x2) isequal(x1,x2), B, repmat({A},size(B,1),1)),:)
C = horzcat(excl_rows{:})
Or another cellfun based approach that avoids repmat -
exclB = B(~cellfun(#(x1) isequal(x1,A), B),:)
C = horzcat(exclB{:})
Example with explanation -
%// Inputs
A = [1,4,7,6]
B = {[1,4,7,6]
[2,6,9,12]
[3,1,17,13]
[5,7,13,19]
[1,5,9,15]}
%// Compare each cell of B with A for equality.
%// The output must be a binary array where one would be for cells that have
%// elements same as A and zero otherwise.
ind = cellfun(#(x1) isequal(x1,A), B)
%// Thus, ~ind would be a binary array where one would reperesent unequal
%// cells that are to be selected in B for the final output.
exclB = B(~ind)
%// exclB is still a cell array with the cells that are different from A.
%// So, concatenate the elements from exclB into a vector as requested.
C = horzcat(exclB{:})
Output -
A =
1 4 7 6
B =
[1x4 double]
[1x4 double]
[1x4 double]
[1x4 double]
[1x4 double]
ind =
1
0
0
0
0
exclB =
[1x4 double]
[1x4 double]
[1x4 double]
[1x4 double]
C =
2 6 9 12 3 1 17 13 5 7 13 19 1 5 9 15

MATLAB: Structure containing an column vector won't display the vector

I have defined a structures array as such:
oRandVecs = struct('vV',{[],[]},...
'ind_min',{[],[]},...
'mean',{[],[]},...
'vV_descending',{[],[]},...
'largest_diff',{[],[]});
oRandVecs(1).vV and oRandVecs(2).vv both get column vectors assigned to them. However, the problem is that the output is as follows:
>> oRandVecs(1)
ans =
vV: [4x1 double]
ind_min: 2
mean: 6.5500
vV_descending: [4x1 double]
largest_diff: 2.8000
Instead of actually showing the vector, it only describes its type.
What am I to do?
The reason why is because it's simply too big to display on the screen with that structure :) If you want to actually display it, use dot notation to display your data.
In other words, do this:
disp(oRandVecs(1).vV);
You can also do that with the other variable:
disp(oRandVecs(1).vV_descending);
The answer by rayryeng is probably the way to go.
An alternative is to convert from structure to cell and then use celldisp:
celldisp(struct2cell(oRandVecs(1)))
Example:
>> oRandVecs = struct('vV',{[],[]},...
'ind_min',{[],[]},...
'mean',{[],[]},...
'vV_descending',{[],[]},...
'largest_diff',{[],[]}); %// define empty struct
>> oRandVecs(1).vV = (1:4).'; %'// fill some fields: column vector, ...
>> oRandVecs(1).mean = 5:7; %// ... row vector, ...
>> oRandVecs(1).vV_descending = randn(2,3); %// ... matrix
>> celldisp(struct2cell(oRandVecs(1)))
ans{1} =
1
2
3
4
ans{2} =
[]
ans{3} =
5 6 7
ans{4} =
0.016805198757746 0.236095190511728 0.735153386198679
2.162769508502985 -0.158789830267017 0.661856091557715
ans{5} =
[]

How can I group variables from a mat file by name and collect them in a structure?

I'm reading in a mat file which has variables with a counter e.g. a1, a2, a3, b, c1 and c2.
I want to put the data into a structure with field names "a", "b" and "c" where
a=[a1;a2;a3], b=b and c = [c1; c2].
The algorithm needs to be able to do this, without knowing what the maximum value of the counter of each variable is. How can I do this?
First, you would load your mat file data using the load function, placing it in a structure. Let's say that gave you the following sample data:
>> s = struct('a1',1:5,'b',[],'a2',1:5,'a3',1:5,'c1',1:3,'c2',3:5)
s =
a1: [1 2 3 4 5]
b: []
a2: [1 2 3 4 5]
a3: [1 2 3 4 5]
c1: [1 2 3]
c2: [3 4 5]
Then, we'll order the fields alphabetically using orderfields, find the field names using fieldnames, use regexprep to strip all trailing numbers off of the field names, then get the unique substrings using unique:
>> s = orderfields(s);
>> [strs, inds] = unique(regexprep(fieldnames(s), '\d*$', ''), 'last')
strs =
'a'
'b'
'c'
inds =
3
4
6
Using the indices returned by unique, we can calculate how many times each substring appears by doing diff([0; inds]). Then, we group the structure data into a cell array using these counts and struct2cell and mat2cell:
>> data = mat2cell(struct2cell(s), diff([0; inds]))
data =
{3x1 cell}
{1x1 cell}
{2x1 cell}
Notice that we have a cell array of cell arrays. If you know for sure that each set of entries for each substring will concatenate properly (as in our example), you can concatenate them using cellfun and cell2mat as follows:
>> data = cellfun(#cell2mat, data, 'UniformOutput', false)
data =
[3x5 double]
[]
[2x3 double]
Now we have a cell array of matrices, and a new structure can be made using cell2struct:
>> snew = cell2struct(data, strs)
snew =
a: [3x5 double]
b: []
c: [2x3 double]

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 :)