Flatten MATLAB structure using dynamic structure reference in single line - matlab

I have the following structure:
myStruct.A.var = 'abc';
myStruct.B.var = '123';
myStruct.C.var = 7;
...
I am looking for a compact (1-2 lines) way of flattening the structure in order to get all the values of the 'var' subfields as:
flattened_var = {'abc', '123', 7}
For example, something like the below lines (which sadly do not work):
inBetweenFields = {'A', 'B', 'C'};
flattened_var = myStruct.(inBetweenFields{:}).var;

A simple loop is more than adequate:
% Set up data
myStruct.A.var = 'abc';
myStruct.B.var = '123';
myStruct.C.var = 7;
% Extract data
substructs = fieldnames(myStruct); % Get substructure names
nsubs = numel(substructs); % Get number of substructures
flatteneddata = cell(nsubs, 1); % Preallocate cell array
% Extract data
for ii = 1:nsubs
flatteneddata{ii} = myStruct.(substructs{ii}).var;
end
This can also be made Twitter compatible with cellfun, which is very nearly always slower than the equivalent loop.
% Set up data
myStruct.A.var = 'abc';
myStruct.B.var = '123';
myStruct.C.var = 7;
flatteneddata = cellfun(#(x) myStruct.(x).var, fieldnames(myStruct), 'UniformOutput', false);
Both approaches generate the desired output:
flatteneddata =
3×1 cell array
'abc'
'123'
[ 7]

try this:
cellfun(#struct2cell,struct2cell(myStruct))

Related

All possible combinations of many parameters MATLAB

I have a list of parameters and I need to evaluate my method over this list. Right now, I am doing it this way
% Parameters
params.corrAs = {'objective', 'constraint'};
params.size = {'small', 'medium', 'large'};
params.density = {'uniform', 'non-uniform'};
params.k = {3,4,5,6};
params.constraintP = {'identity', 'none'};
params.Npoints_perJ = {2, 3};
params.sampling = {'hks', 'fps'};
% Select the current parameter
for corrAs_iter = params.corrAs
for size_iter = params.size
for density_iter = params.density
for k_iter = params.k
for constraintP_iter = params.constraintP
for Npoints_perJ_iter = params.Npoints_perJ
for sampling_iter = params.sampling
currentParam.corrAs = corrAs_iter;
currentParam.size = size_iter;
currentParam.density = density_iter;
currentParam.k = k_iter;
currentParam.constraintP = constraintP_iter;
currentParam.Npoints_perJ = Npoints_perJ_iter;
currentParam.sampling = sampling_iter;
evaluateMethod(currentParam);
end
end
end
end
end
end
end
I know it looks ugly and if I want to add a new type of parameter, I have to write another for loop. Is there any way, I can vectorize this? Or maybe use 2 for loops instead of so many.
I tried the following but, it doesn't result in what I need.
for i = 1:numel(fields)
% if isempty(params.(fields{i}))
param.(fields{i}) = params.(fields{i})(1);
params.(fields{i})(1) = [];
end
What you need is all combinations of your input parameters. Unfortunately, as you add more parameters the storage requirements will grow quickly (and you'll have to use a large indexing matrix).
Instead, here is an idea which uses linear indicies of a (never created) n1*n2*...*nm matrix, where ni is the number of elements in each field, for m fields.
It is flexible enough to cope with any amount of fields being added to params. Not performance tested, although as with any "all combinations" operation you should be wary of the non-linear increase in computation time as you add more fields to params, note prod(sz)!
The code I've shown is fast, but the performance will depend entirely on which operations you do in the loop.
% Add parameters here
params.corrAs = {'objective', 'constraint'};
params.size = {'small', 'medium', 'large'};
params.density = {'uniform', 'non-uniform'};
% Setup
f = fieldnames( params );
nf = numel(f);
sz = NaN( nf, 1 );
% Loop over all parameters to get sizes
for jj = 1:nf
sz(jj) = numel( params.(f{jj}) );
end
% Loop for every combination of parameters
idx = cell(1,nf);
for ii = 1:prod(sz)
% Use ind2sub to switch from a linear index to the combination set
[idx{:}] = ind2sub( sz, ii );
% Create currentParam from the combination indices
currentParam = struct();
for jj = 1:nf
currentParam.(f{jj}) = params.(f{jj}){idx{jj}};
end
% Do something with currentParam here
% ...
end
Asides:
I'm using dynamic field name references for indexing the fields
I'm passing multiple outputs into a cell array from ind2sub, so you can handle a variable number of field names when ind2sub has one output for each dimension (or field in this use-case).
Here is a vectorized solution :
names = fieldnames(params).';
paramGrid = cell(1,numel(names));
cp = struct2cell(params);
[paramGrid{:}] = ndgrid(cp{:});
ng = [names;paramGrid];
st = struct(ng{:});
for param = st(:).'
currentParam = param;
end
Instead of nested loops we can use ndgrid to create the cartesian product of the cell entries so we can find all combinations of cell entries without loop.

flatten a struct of arbitrarily nested arrays of integers into a flat array of integers

Is it possible to flatten an array of arbitrarily nested arrays of integers into a flat array of integers in Matlab? For example,
[[1,2,[3]],4] -> [1,2,3,4]
Any kind of guidance will be helpful. Thanks.
For example,
a.c = [5,4];
a.b.a=[9];
a.b.d=[1,2];
a= b: [1x1 struct]
c: [5 4]
In this case, my output will be
output= [9,1,2,5,4]
I think you will have to adapt the flatten function from the file exchange to use struct2cell so something like this:
function C = flatten_struct(A)
A = struct2cell(A);
C = [];
for i=1:numel(A)
if(isstruct(A{i}))
C = [C,flatten_struct(A{i})];
else
C = [C,A{i}];
end
end
end
This results in:
a.c = [5,4];
a.b.a=[9];
a.b.d=[1,2];
flatten_struct(a)
ans =
5 4 9 1 2
So the order is in the order you declared your struct instead of in your example order which I presume is alphabetical. But you have control over this so it shouldn't be a problem.
I have a preliminary hack which does work but rather clumsily. It descends recursively, saving structure names and unpacking the returned structure at each "level" .
% struct2sims converter
function simout = struct2sims(structin)
fnam = fieldnames(structin);
for jf = 1:numel(fnam)
subnam = [inputname(1),'_',fnam{jf}];
if isstruct(structin.(fnam{jf}) ) ,
% need to dive; build a new variable that's not a substruct
eval(sprintf('%s = structin.(fnam{jf});', fnam{jf}));
eval(sprintf('simtmp = struct2sims(%s);',fnam{jf}) );
% try removing the struct before getting any farther...
simout.(subnam) = simtmp;
else
% at bottom, ok
simout.(subnam) = structin.(fnam{jf});
end
end
% need to unpack structs here, after each level of recursion
% returns...
subfnam = fieldnames(simout);
for kf = 1:numel(subfnam)
if isstruct(simout.(subfnam{kf}) ),
subsubnam = fieldnames(simout.(subfnam{kf}));
for fk = 1:numel(subsubnam)
simout.([inputname(1),'_',subsubnam{fk}])...
= simout.(subfnam{kf}).(subsubnam{fk}) ;
end
simout = rmfield(simout,subfnam{kf});
end
end
% if desired write to file with:
% save('flattened','-struct','simout');
end

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

Dynamically name a struct variable in MATLAB

I have several files "2011-01-01.txt", "2013-01-02.txt", "2015-02-01.txt", etc.
I wish to create a struct variable for each of the file such that (the values are made up):
machine20110101.size=[1,2,3];
machine20110101.weight=2345;
machine20110101.price=3456;
machine20130102.size=[2,3,4];
machine20130102.weight=1357;
machine20130102.price=123;
machine20150201.size=[1,2,4];
machine20150201.weight=1357;
machine20150201.price=123;
And,
save('f20110101.mat','machine20110101');
save('f20130102.mat','machine20130102') ;
save('f20150201.mat','machine20150201');
As we can see, the struct names are derived from the files' names. How can I construct the above struct variables?
I've searched for a while, but I didn't figure out how to use genvarname.
And these links (dynamic variable names in matlab, dynamic variable declaration function matlab, Dynamically change variable name inside a loop in MATLAB) didn't solve my problem.
I'm using MATLAB R2012b, so functions like matlab.lang.makeUniqueStrings defined after this version is unavailable.
Now that I'm in front of MATLAB, here's an example based on my comment above, utilizing dynamic field names with the filenames pruned using fileparts and regexprep in a cellfun call.
% Sample list for testing here, use uigetdir with dir or whatever method to
% get a list of files generically
filelist = {'C:\mydata\2011-01-01.txt', ...
'C:\mydata\2012-02-02.txt', ...
'C:\mydata\2013-03-03.txt', ...
'C:\mydata\2014-04-04.txt', ...
};
nfiles = length(filelist);
% Get filenames from your list of files
[~, filenames] = cellfun(#fileparts, filelist, 'UniformOutput', false);
% Prune unwanted characters from each filename and concatenate with 'machine'
prunedfilenames = regexprep(filenames, '-', '');
myfieldnames = strcat('machine', prunedfilenames);
% Generate your structure
for ii = 1:nfiles
% Parse your files for the data, using dummy variables since I don't
% know how your data is structured
loadedsize = [1, 2, 3];
loadedweight = 1234;
loadedprice = 1234;
% Add data to struct array
mydata.(myfieldnames{ii}).size = loadedsize;
mydata.(myfieldnames{ii}).weight = loadedweight;
mydata.(myfieldnames{ii}).price = loadedprice;
end
#patrik raises some good points in the comments. I think the more generic method he would like to see (please correct me if I'm wrong) goes something like this:
% Sample list for testing here, use uigetdir with dir or whatever method to
% get a list of files generically
filelist = {'C:\mydata\2011-01-01.txt', ...
'C:\mydata\2012-02-02.txt', ...
'C:\mydata\2013-03-03.txt', ...
'C:\mydata\2014-04-04.txt', ...
};
nfiles = length(filelist);
% Get filenames from your list of files
[~, filenames] = cellfun(#fileparts, filelist, 'UniformOutput', false);
% Prune unwanted characters from each filename and concatenate with 'machine'
prunedfilenames = regexprep(filenames, '-', '');
mytags = strcat('machine', prunedfilenames);
% Preallocate your structure
mydata = repmat(struct('tag', '', 'size', [1, 1, 1], 'weight', 1, 'price', 1), nfiles, 1);
% Fill your structure
for ii = 1:nfiles
% Parse your files for the data, using dummy variables since I don't
% know how your data is structured
loadedsize = [1, 2, 3];
loadedweight = 1234;
loadedprice = 1234;
% Add data to struct array
mydata(ii).tag = mytags{ii};
mydata(ii).size = loadedsize;
mydata(ii).weight = loadedweight;
mydata(ii).price = loadedprice;
end
Besides #excaza's answer, I used the following approach:
machine.size = [1,2,3]; machine.price = 335; machine.weight = 234;
machineName = ['machine',the_date];
machineSet = struct(machineName,machine);
save(OutputFile,'-struct','machineSet',machineName);

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]