Iterating structures array field's values - matlab

How to iterate/loop structures array field's values.
For 1x1 struct
student = struct();
student.name = 'jim';
student.gpa = 1.9;
I do this :
fields = fieldnames(student)
for i=1:numel(fields)
var = fields(i)
end
But I don't how to iterate 1 x 2 :
student = struct();
student(1).name = 'jim';
student(1).gpa = 1.9;
student(2).name = 'ryan';
student(2).gpa = 1.5;

You need to have either another for loop
fields = fieldnames(student);
for k = 1:numel(student)
for m = 1:numel(fields)
var = student(k).(fields{m});
end
end
Alternately, you can use the fact that the dot notation will create a comma separated list and you can place them in either a cell array (for strings) or a normal array
names = {student.name};
gpas = [student.name];
I typically prefer to use the second approach most often for accessing the same field from a struct array.

Related

Checking fields of a structure for empties then assigning them default values

Below is an example what I would like to do. Given two structure arrays, I want to search test_value structure to see if any fields are empty. If a field is empty, I want to replace the empty with the corresponding value in the default_value structure.
test_values = struct();
test_values.x = 2;
test_values.y = [1 0 4];
test_values.z = [];
default_values = struct();
default_values.x = 0;
default_values.y = [1 1 1];
default_values.z = 2;
% Check if empty. I want to check every field in the structure but too many
% fields for this approach.
if isempty(test_values.z)
test_values.z = default_values.z;
end
Does anyone know of better way then using if statements for each field? I appreciate any help you may provide. Thanks.
You can get all the fields using fieldnames and simply loop through
f = fieldnames( test_values );
for ii = 1:numel(f)
if isempty( test_values.(f{ii}) )
% Note the use of the .(___) notation to index a field with a string variable
test_values.(f{ii}) = default_values.(f{ii});
end
end
You could also use isfield to check the field has a default, before trying to apply it!

Matlab: How do I create multiple fields in a struct with a for loop

I need to create a bunch of fields in a struct with names that differ only by a digit, like this:
S(1).field1 = [];
S(1).field2 = [];
S(1).field3 = [];
S(1).field4 = [];
S(1).field5 = [];
This is a short version of the list. The real one is actually long and doesn't look pretty in the script so I am wondering whether I can initiate those empty fields within a for loop. I tried:
for i = 1:5
S(1).field{i} = [];
end
but it doesn't work.
Use dynamic field names: this means that S.field1 is exactly the same as S.('field1'). In action:
for k = 1:5
S(1).(['field' num2str(k)]) = [];
end
Note that I changed the loop variable to k: num2str(i) could also return 0+1i if you're not careful.
There are also some more funky, seemingly loopless solutions, such as:
n = 5;
S = cell2struct(cell(1,5),...
arrayfun(#(x) ['field' num2str(x)],1:n,'uniformoutput',false),...
2);
This will create a cell {[],[],[],[],[]} for the field values, and another cell {'field1','field2',...,'field5'} for the field names, and constructs a struct from these.

Matlab loop through functions using an array in a for loop

I am writing a code to do some very simple descriptive statistics, but I found myself being very repetitive with my syntax.
I know there's a way to shorten this code and make it more elegant and time efficient with something like a for-loop, but I am not quite keen enough in coding (yet) to know how to do this...
I have three variables, or groups (All data, condition 1, and condition 2). I also have 8 matlab functions that I need to perform on each of the three groups (e.g mean, median). I am saving all of the data in a table where each column corresponds to one of the functions (e.g. mean) and each row is that function performed on the correspnding group (e.g. (1,1) is mean of 'all data', (2,1) is mean of 'cond 1', and (3,1) is mean of 'cond 2'). It is important to preserve this structure as I am outputting to a csv file that I can open in excel. The columns, again, are labeled according the function, and the rows are ordered by 1) all data 2) cond 1, and 3) cond 2.
The data I am working with is in the second column of these matrices, by the way.
So here is the tedious way I am accomplishing this:
x = cell(3,8);
x{1,1} = mean(alldata(:,2));
x{2,1} = mean(cond1data(:,2));
x{3,1} = mean(cond2data(:,2));
x{1,2} = median(alldata(:,2));
x{2,2} = median(cond1data(:,2));
x{3,2} = median(cond2data(:,2));
x{1,3} = std(alldata(:,2));
x{2,3} = std(cond1data(:,2));
x{3,3} = std(cond2data(:,2));
x{1,4} = var(alldata(:,2)); % variance
x{2,4} = var(cond1data(:,2));
x{3,4} = var(cond2data(:,2));
x{1,5} = range(alldata(:,2));
x{2,5} = range(cond1data(:,2));
x{3,5} = range(cond2data(:,2));
x{1,6} = iqr(alldata(:,2)); % inter quartile range
x{2,6} = iqr(cond1data(:,2));
x{3,6} = iqr(cond2data(:,2));
x{1,7} = skewness(alldata(:,2));
x{2,7} = skewness(cond1data(:,2));
x{3,7} = skewness(cond2data(:,2));
x{1,8} = kurtosis(alldata(:,2));
x{2,8} = kurtosis(cond1data(:,2));
x{3,8} = kurtosis(cond2data(:,2));
% write output to .csv file using cell to table conversion
T = cell2table(x, 'VariableNames',{'mean', 'median', 'stddev', 'variance', 'range', 'IQR', 'skewness', 'kurtosis'});
writetable(T,'descriptivestats.csv')
I know there is a way to loop through this stuff and get the same output in a much shorter code. I tried to write a for-loop but I am just confusing myself and not sure how to do this. I'll include it anyway so maybe you can get an idea of what I'm trying to do.
x = cell(3,8);
data = [alldata, cond2data, cond2data];
dfunction = ['mean', 'median', 'std', 'var', 'range', 'iqr', 'skewness', 'kurtosis'];
for i = 1:8,
for y = 1:3
x{y,i} = dfucntion(i)(data(1)(:,2));
x{y+1,i} = dfunction(i)(data(2)(:,2));
x{y+2,i} = dfunction(i)(data(3)(:,2));
end
end
T = cell2table(x, 'VariableNames',{'mean', 'median', 'stddev', 'variance', 'range', 'IQR', 'skewness', 'kurtosis'});
writetable(T,'descriptivestats.csv')
Any ideas on how to make this work??
You want to use a cell array of function handles. The easiest way to do that is to use the # operator, as in
dfunctions = {#mean, #median, #std, #var, #range, #iqr, #skewness, #kurtosis};
Also, you want to combine your three data variables into one variable, to make it easier to iterate over them. There are two choices I can see. If your data variables are all M-by-2 in dimension, you could concatenate them into a M-by-2-by-3 three-dimensional array. You could do that with
data = cat(3, alldata, cond1data, cond2data);
The indexing expression into data that retrieves the values you want would be data(:, 2, y). That said, I think this approach would have to copy a lot of data around and probably isn't the best for performance. The other way to combine data together is in 1-by-3 cell array, like this:
data = {alldata, cond1data, cond2data};
The indexing expression into data that retrieves the values you want in this case would be data{y}(:, 2).
Since you are looping from y == 1 to y == 3, you only need one line in your inner loop body, not three.
for y = 1:3
x{y, i} = dfunctions{i}(data{y}(:,2));
end
Finally, to get the cell array of strings containing function names to pass to cell2table, you can use cellfun to apply func2str to each element of dfunctions:
funcnames = cellfun(#func2str, dfunctions, 'UniformOutput', false);
The final version looks like this:
dfunctions = {#mean, #median, #std, #var, #range, #iqr, #skewness, #kurtosis};
data = {alldata, cond1data, cond2data};
x = cell(length(data), length(dfunctions));
for i = 1:length(dfunctions)
for y = 1:length(data)
x{y, i} = dfunctions{i}(data{y}(:,2));
end
end
funcnames = cellfun(#func2str, dfunctions, 'UniformOutput', false);
T = cell2table(x, 'VariableNames', funcnames);
writetable(T,'descriptivestats.csv');
You can create a cell array of functions using str2func :
function_string = {'mean', 'median', 'std', 'var', 'range', 'iqr', 'skewness', 'kurtosis'};
dfunction = {};
for ii = 1:length(function_string)
fun{ii} = str2func(function_string{ii})
end
Then you can use it on your data as you'd like to :
for ii = 1:8,
for y = 1:3
x{y,i} = dfucntion{ii}(data(1)(:,2));
x{y+1,i} = dfunction{ii}(data(2)(:,2));
x{y+2,i} = dfunction{ii}(data(3)(:,2));
end
end

Write field name and value form structures to a file

I am wondering how can I write a full structure in an output file *.txt with all fieldnames and the respectives values. For example
allvariables=
names='raw.txt'
date= = '**/**/2013'
User = Mr/Mrs *
timeProcessing = 20
numberIterations = 1000;
Should be written in output.txt with all the information displayed before.
This is just an example but my structure has a length of 50 fieldnames, so I would appreciate any suggestion!
Here's something you can use:
%// Extract field data
fields = fieldnames(allvariables);
values = struct2cell(allvariables);
%// Optional: add enclosing apostrophes around string values
idx = cellfun(#ischar, values);
values(idx) = cellfun(#(x){['''', x, '''']}, values(idx));
%// Convert numerical values to strings
idx = cellfun(#isnumeric, values);
values(idx) = cellfun(#num2str, values(idx), 'UniformOutput', false);
%// Convert cell arrays of strings to comma-delimited strings
idx = cellfun(#iscellstr, values);
stringify_cellstr = #(x){['{' sprintf('''%s'', ', x{1:end - 1}) ...
sprintf('''%s''', x{end}) '}']};
values(idx) = cellfun(stringify_cellstr, values(idx));
%// Convert cell array of numbers to strings
idx = cellfun(#iscell, values);
isnumber = #(x)isnumeric(x) && isscalar(x);
idx_num = cellfun(#(x)all(arrayfun(#(k)isnumber(x{k}),1:numel(x))), values(idx));
idx(idx) = idx_num;
stringify_cellnum = #(x){['{' sprintf('%d, ' x{1:end - 1}) num2str(x{end}) '}']};
values(idx) = cellfun(stringify_cellnum, values(idx));
%// Combine field names and values in the same array
C = {fields{:}; values{:}};
%// Write fields to text file
fid = fopen('output.txt', 'wt');
fprintf(fid, repmat('%s = %s\n', 1, size(C, 2)), C{:});
fclose(fid);
This solution is essentially a variant of this one. Note that it assumes that each field contains a scalar value or a string, but it can be extended to handle other types, of course.
EDIT: added handling for fields storing cell arrays of strings and cell arrays of numbers.

build and use hash table using containers.Map

I'm trying to create an expanding lookup table. I think the cell array is what I want but I'm not sure. The structure would be initialized with rows, and an unknown amount of columns. I want to be able to append to the end of each row, and access all values from a specific row.
desired structure:
[1] [4,5] [6,7]
[2] [4,5] [6,7] [3,6]
...
[n] [R1,B2] [R2,B2] ... [Rm, Bm]
this is what I have so far
%%% Build the R-table
n = 360;
k = {};
v = {};
for i = 1:n
k{end+1} = i; % how would I get n keys without this loop?
v{end+1} = {}; % how would I get n values without this loop?
end
rTable = containers.Map(k, v);
%%% add R,B pair to key I
I = 1;
R_add = 4;
B_add = 5;
current_list_temp = rTable(I); % can I add without using a temp variable?
current_list_temp{end+1} = {[R_add, B_add]};
rTable(I) = current_list_temp;
%%% read values for Nth pair in the Ith key
I = 1;
N = 1;
temp = rTable(I); % can I read the values without using a temp variable?
R_read = temp{N}{1}(1);
B_read = temp{N}{1}(2);
Is there a better way of doing this?
When used for indexing end translates to the largest allowed index and you can manipulate it by adding or multiplying it, so instead of
first_empty_cell = ?
cell{index, first_free_cell} = [4,5]
try
cell{index, end+1} = [4,5]