Simultaneously assign values to multiple structure fields - matlab

I have a matlab structure that follows the following pattern:
S.field1.data1
...
.field1.dataN
...
.fieldM.data1
...
.fieldM.dataN
I would like to assign values to one data field (say, data3) from all fields simultaneously. That would be semantically similar to:
S.*.data3 = value
Where the wildcard "*" represents all fields (field1,...,fieldM) in the structure. Is this something that can be done without a loop in matlab?

Since field1 .. fieldM are structure arrays with identical fields, why not make a struct array for "field"? Then you can easily set all "data" members to a specific value using deal.
field(1).data1 = 1;
field(1).data2 = 2;
field(2).data1 = 3;
field(2).data2 = 4;
[field.data1] = deal(5);
disp([field.data1]);

A loop-based solution can be flexible and easily readable:
names = strtrim(cellstr( num2str((1:5)','field%d') )); %'# field1,field2,...
values = num2cell(1:5); %# any values you want
S = struct();
for i=1:numel(names)
S.(names{i}).data3 = values{i};
end

In simple cases, you could do that by converting your struct into a cell array using struct2cell(). As you have a nested structure, I don't think that will work here.
On the other side, is there any reason why your data is structured like this. Your description gives the impression that a simple MxN array or cell array would be more suitable.

Related

MATLAB: Pass part of structure field name to function

I need to pass a part of a structure's name into a function.
Examples of a available structs:
systems.system1.stats.equityCurve.relative.exFee
systems.system1.stats.equityCurve.relative.inFee
systems.system2.stats.equityCurve.relative.exFee
systems.system2.stats.equityCurve.relative.inFee
systems.system1.returns.aggregated.exFee
systems.system1.returns.aggregated.inFee
systems.system2.returns.aggregated.exFee
systems.system2.returns.aggregated.inFee
... This goes on...
Within a function, I loop through the structure as follows:
function mat = test(fNames)
feeString = {'exFee', 'inFee'};
sysNames = {'system1', 'system2'};
for n = 1 : 2
mat{n} = systems.(sysNames{n}).stats.equityCurve.relative.(feeString{n});
end
end
What I like to handle in a flexible way within the loop is the middle part, i.e. the part after systems.(sysNames{n}) and before .(feeString{n}) (compare examples).
I am now looking for a way to pass the middle part as an input argument fNames into the function. The loop should than contain something like
mat{n} = systems.(sysNames{n}).(fName).(feeString{n});
How about using a helper function such as
function rec_stru = recSA(stru, field_names)
if numel(field_names) == 1
rec_stru = stru.(field_names{1});
else
rec_stru = recSA(stru.(field_names{1}), field_names(2:end));
end
This function takes the intermediate field names as a cell array.
This would turn this statement:
mat{n} = systems.(sysNames{n}).stats.equityCurve.relative.(feeString{n});
into
mat{n} = recSA(systems.(sysNames{n}), {'stats', 'equityCurve', 'relative', feeString{n}});
The first part of the cell array could then be passed as an argument to the function.
This is one of those cases where matlab is a bit unhelpful in the documentation. There is a way to use the fieldnames function in matlab to get the list of all the fields and iterate over that using dynamic fields.
systems.system1.stats.equityCurve.relative.exFee='T'
systems.system1.stats.equityCurve.relative.inFee='E'
systems.system2.stats.equityCurve.relative.exFee='S'
systems.system2.stats.equityCurve.relative.inFee='T'
systems.system1.returns.aggregated.exFee='D'
systems.system1.returns.aggregated.inFee='A'
systems.system2.returns.aggregated.exFee='T'
systems.system2.returns.aggregated.inFee='A'
dynamicvariable=fieldnames(systems.system1)
This will return a cell matrix of the field names which you can use to iterate over.
systems.system1.(dynamicvariable{1})
ans =
equityCurve: [1x1 struct]
Ideally you would have your data structure fixed in such a way that you know how many levels of depth are in your data structure.

Matlab: iterate over multiple structs

I have 5 Matlab structs. I would like to iterate over them. My current solution is the following:
all_structs = [struct1,struct2,struct3,struct4,struct5];
for single_struct = all_structs
% do stuff to each struct here
end
However, each of the structs above has a matrix with a lot of data (including some other properties). Also, whatever I change in the single_struct is not passed back to the original struct.
Question: How do I fix that? Does Matlab copy all that data again when I create the vector all_structs? Or is the data from each of the structs (struct1,...,struct5) passed by reference? Is there a better way to iterate over multiple structs?
Thanks for helping!
struct will not be passed by reference. You will need to loop over the elements in all_structs using an index and then access and modify using that index. If you need something to be treated as reference you will need to define a class for it and make the class inherit from handle. Suggested reading
for i = 1:numel(all_structs)
% do stuff to each struct here
all_structs(i).data = ones(10,5); % your code here
end
I would suggest also reading on arrayfun, though it is useful if you want to do an operation and get results. From your description it sounds like you want to modify the structs.
In case you want to modify content of original structs, without making a copy, you can use a cell array of structs names.
Then iterate the names, and use eval to modify the content.
Using eval is inefficient, so don't make it a habit...
See the following code sample:
%Create sample structs (each struct has a data element).
struct1.data = 1;
struct2.data = 2;
struct3.data = 3;
%Create a cell array containing structs names as strings.
struct_names = {'struct1', 'struct2', 'struct3'};
%Iterate all structs names
%Modify data elements of each struct using eval.
for i = 1:length(struct_names)
sname = struct_names{i}; %Get struct name from cell array.
%Evaluate a string like: 'struct1.data = struct1.data + 1;'
eval([sname, '.data = ', sname, '.data + 1;']);
end

Using a string to refer to a structure array - matlab

I am trying to take the averages of a pretty large set of data, so i have created a function to do exactly that.
The data is stored in some struct1.struct2.data(:,column)
there are 4 struct1 and each of these have between 20 and 30 sub-struct2
the data that I want to average is always stored in column 7 and I want to output the average of each struct2.data(:,column) into a 2xN array/double (column 1 of this output is a reference to each sub-struct2 column 2 is the average)
The omly problem is, I can't find a way (lots and lots of reading) to point at each structure properly. I am using a string to refer to the structures, but I get error Attempt to reference field of non-structure array. So clearly it doesn't like this. Here is what I used. (excuse the inelegence)
function [avrg] = Takemean(prefix,numslits)
% place holder arrays
avs = [];
slits = [];
% iterate over the sub-struct (struct2)
for currslit=1:numslits
dataname = sprintf('%s_slit_%02d',prefix,currslit);
% slap the average and slit ID on the end
avs(end+1) = mean(prefix.dataname.data(:,7));
slits(end+1) = currslit;
end
% transpose the arrays
avs = avs';
slits = slits';
avrg = cat(2,slits,avs); % slap them together
It falls over at this line avs(end+1) = mean(prefix.dataname.data,7); because as you can see, prefix and dataname are strings. So, after hunting around I tried making these strings variables with genvarname() still no luck!
I have spent hours on what should have been 5min of coding. :'(
Edit: Oh prefix is a string e.g. 'Hs' and the structure of the structures (lol) is e.g. Hs.Hs_slit_XX.data() where XX is e.g. 01,02,...27
Edit: If I just run mean(Hs.Hs_slit_01.data(:,7)) it works fine... but then I cant iterate over all of the _slit_XX
If you simply want to iterate over the fields with the name pattern <something>_slit_<something>, you need neither the prefix string nor numslits for this. Pass the actual structure to your function, extract the desired fields and then itereate them:
function avrg = Takemean(s)
%// Extract only the "_slit_" fields
names = fieldnames(s);
names = names(~cellfun('isempty', strfind(names, '_slit_')));
%// Iterate over fields and calculate means
avrg = zeros(numel(names), 2);
for k = 1:numel(names)
avrg(k, :) = [k, mean(s.(names{k}).data(:, 7))];
end
This method uses dynamic field referencing to access fields in structs using strings.
First of all, think twice before you use string construction to access variables.
If you really really need it, here is how it can be used:
a.b=123;
s1 = 'a';
s2 = 'b';
eval([s1 '.' s2])
In your case probably something like:
Hs.Hs_slit_01.data= rand(3,7);
avs = [];
dataname = 'Hs_slit_01';
prefix = 'Hs';
eval(['avs(end+1) = mean(' prefix '.' dataname '.data(:,7))'])

Outputting data from for loop to .mat file using numbers in title MATLAB

I need to output .mat files for the below data. I need one file to have cell (1,1) to be Mean_RPM_list1, cell (2,1) to be Mean_RPM_list2 etc. And then I need another file to have cell(1,1) to be Mean_Torque_list1 to have cell(1,1).....and so on.
Can anybody shed any light on this for me?
Also if someone knows how to automate me calling the matrices A and B so I could have A = [Mean_rpm1:Mean_rpmMAX], that would also be very helpful.
TIA for any help.
A = [Mean_rpm1 Mean_rpm2 Mean_rpm3 Mean_rpm4 Mean_rpm5 Mean_rpm6 Mean_rpm7 Mean_rpm8 Mean_rpm9 Mean_rpm10 Mean_rpm11 Mean_rpm12];
B = [Mean_torque1 Mean_torque2 Mean_torque3 Mean_torque4 Mean_torque5 Mean_torque6 Mean_torque7 Mean_torque8 Mean_torque9 Mean_torque10 Mean_torque11 Mean_torque12];
plot(A,B,'*')
for i = 1:num_bins;
bin = first + ((i-1)/10);
eval(sprintf('Mean_RPM_list%0.f = A;',bin*10));
eval(sprintf('Mean_Torque_list%0.f = B;',bin*10));
end
First of all this is really bad idea to create a set of variables with names different by numbers. As you can see it's very difficult to deal with such variables, you always have to use eval (or other related) statements.
It's much easier to create a cell array Mean_rpm and access its elements as Mean_rpm{1}, etc.
If the vectors are numeric and have the same size you can also make a 2D/3D array. Then access as Mean_rpm(:,:,1) etc.
Next, to store a cell array to a mat-file you have to create this array in MATLAB. No options (at least for now) to do it by parts in a loop. (But you can do it for numeric vectors and matrices using matfile object.) So why do you need this intermediate Mean_RPM_list variable? Just do Mean_RPM_list{bin*10} = A in your loop.
For your first question, if you already have those variables you have to use eval in a loop. Something like
A = [];
for k=1:K
eval(sprintf('A{k} = [A, Mean_rpm%d];',k));
end
You can also get names for all similar variables and combine them.
varlist = who('Mean_rpm*');
A = cell(1,numel(varlist);
for k = 1:numel(varlist)
eval('A{k} = varlist{k};');
end
Here is one without loop using CELL2FUN:
A=cellfun(#(x)evalin('base',x),varlist,'UniformOutput',0);
You should avoid having all these individual variables around in the first place. Data types like arrays, cell arrays and structure arrays exist to help you with this. If you want each variable to be associated with a name, you can use a structure array. I've made an example below. Instead of assigning a value to Mean_rpm1 like you are doing now, assign it to meanStruct.Mean_rpm1 then save the entire structure.
% as you generate values for each variable, assign them to the
% appropriate field.
meanStruct.Mean_rpm1 = [10:10];
meanStruct.Mean_rpm2 = [12:15];
meanStruct.Mean_rpm3 = [13:20];
meanStruct.Mean_rpm4 = [14];
meanStruct.Mean_rpm5 = [15:18];
meanStruct.Mean_rpm6 = [16:20];
meanStruct.Mean_rpm7 = [17:22];
meanStruct.Mean_rpm8 = [18:22];
meanStruct.Mean_rpm9 = [19:22];
meanStruct.Mean_rpm10 = [20:22];
meanStruct.Mean_rpm11 = [21:22];
meanStruct.Mean_rpm12 = [22:23];
% save the structure array
save('meanValues.mat','meanStruct')
% load and access the structure array
clear all
load('meanValues.mat')
temp = meanStruct.Mean_rpm3

assigning values to a field of an structure array in MATLAB

I want to replace the value of the fields in a structure array. For example, I want to replace all 1's with 3's in the following construction.
a(1).b = 1;
a(2).b = 2;
a(3).b = 1;
a([a.b] == 1).b = 3; % This doesn't work and spits out:
% "Insufficient outputs from right hand side to satisfy comma separated
% list expansion on left hand side. Missing [] are the most likely cause."
Is there an easy syntax for this? I want to avoid ugly for loops for such simple operation.
Credits go to #Slayton, but you actually can do the same thing for assigning values too, using deal:
[a([a.b]==1).b]=deal(3)
So breakdown:
[a.b]
retrieves all b fields of the array a and puts this comma-separated-list in an array.
a([a.b]==1)
uses logical indexing to index only the elements of a that satisfy the constraint. Subsequently the full command above assigns the value 3 to all elements of the resulting comma-separated-list according to this.
You can retrieve that the value of a field for each struct in an array using cell notation.
bVals = {a.b};
bVals = cell2mat( bVals );
AFAIK, you can't do the same thing for inserting values into an array of structs. You'll have to use a loop.