Matlab; Structure field name with valuable ( = number) - matlab

I am trying to assign valuable, which is number and given by for loop, to the name of structure field. For example, I would like to do as following,
A.bx, where A is name of structure(= char), b is part of field name ( = char) and x is valuable given by for loop. A and b is fixed or predefined.
Any comment is appreciated !

genvarname(str,list) generates a valid variable name in str [a string] in which at each iteration value in str is different from the exclusion list
And fieldname(S) returns a list of all the names of the field already in the structure S (use it to create a exclusion list)
Here is a code for what you want:
A = struct ();
for i = 1:5
A.(genvarname ('b', fieldnames (A))) = i;
end
Read about 1. genvarname(str,list) 2. fieldnames(S)

You can name you struct fields using simple sprintf
A = struct()
for ii = 1:10
fn = sprintf('b%d', ii );
A.(fn) = ii; % use the struct
end
I tend to agree with sebastian that suggested using arrays or cells over this type of field naming. In addition to cells and arrays you might find containers.Map to be very versatile and useful.

Related

What is the best practice to recursively extract data from a nested structure in matlab?

I'm trying to exctract some data from a nested structure in a recursive manner. First, I know that this has a field (values) which repeats itself inside the nested structure. Secondly I know that the structure which has those values has only structures as fields. In the below code I've tried to acces the structure.values by searching if my current structure has a field named values. If it has, I put .values at the end of my structure name. If it doesn't have this field, I verify if all the fields are structures. If they are, it means that I will have to consider them further and to extract the values from each one. If the fields are not structures, it means that they are values and I save them into a new simplified structure. Example of fields that I want: S.values.model1.values.mission.values.(alt/list). Currently, with the below code I'm only able to get the values from one field and then I get an error and don't know how to approach further.
Code example:
clear all
clc
S=struct()
S.case='1';
S.type='A';
S.values.model1.case='2'
S.values.model1.type='C'
S.values.model1.values.mission.case='3'
S.values.model1.values.mission.type='D'
S.values.model1.values.mission.values.alt='none'
S.values.model1.values.mission.values.list=2
S.values.model1.values.mission.values.parameter=4
S.values.model1.values.phase.case='4'
S.values.model1.values.phase.type='A'
S.values.model1.values.phase.values.num='all'
S.values.model1.values.phase.values.eq=2
S.values.model1.values.phase.values.unit=4
S.values.model1.values.analysis.case='1'
S.values.model1.values.phase.type='A'
S.values.model1.values.phase.values.nump1.list='all'
S.values.model1.values.phase.values.nump1.table='four'
S.values.model1.values.phase.values.nump1.mean=0
S.values.model1.values.phase.values.nump2.list='none'
S.values.model1.values.phase.values.nump2.table='three';
S.values.model1.values.phase.values.nump2.mean=1
s=S.values.model1;
names=fieldnames(s);
nnames=numel(names);
newStruct={};
[valsi,newstructi]=extractValues(names,s,nnames,newStruct)
function [vals,newStruct]=extractValues(names,vals,nnames,newStruct)
if any(strcmp(names,'values'))
vals=vals.('values');
names=fieldnames(vals)
nnames=numel(names)
[vals,newStruct]=extractValues(names,vals,nnames,newStruct);
end
for j=1:nnames
value(j)=isstruct((vals.(names{j})));
end
if all(value)
for k=1:nnames
vals=(vals.(names{k}));
names=fieldnames(vals);
nnames=numel(names);
[vals,newStruct]=extractValues(names,vals,nnames,newStruct);
end
else
for j=1:nnames
value=(vals.(names{j}));
newStruct.(names{j})=value;
end
end
end
As it is known beforehand what fields are requested you can arrange the subsequent filed names in a cell array and use a loop to extract the value:
names = {'values', 'model1', 'values', 'mission', 'values', 'alt'};
out = S;
for name : names
out = out.(name{1});
end
So that is a loop version of using:
out = S.values.model1.values.mission.values.alt;
EDIT:
If you want to list all field names and all field values you can used these functions:
function out = names(s, p)
if isstruct(s)
out = {};
f = fieldnames(s);
for i = 1:numel(f)
s1 = s.(f{i});
p1 = [p '.' f{i}];
out = [out; names(s1, p1)];
end
else
out = {p};
end
end
function out = values(s)
if isstruct(s)
out = {};
f = fieldnames(s);
for i = 1:numel(f)
out = [out; values(s.(f{i}))];
end
else
out = {s};
end
end
Use them as:
n = names(S, 'S');
v = values(S);

MATLAB: Loop through the values of a list from 'who' function

I have a long list of variables in my workspace.
First, I'm finding the potential variables I could be interested in using the who function. Next, I'd like to loop through this list to find the size of each variable, however who outputs only the name of the variables as a string.
How could I use this list to refer to the values of the variables, rather than just the name?
Thank you,
list = who('*time*')
list =
'time'
'time_1'
'time_2'
for i = 1:size(list,1);
len(i,1) = length(list(i))
end
len =
1
1
1
If you want details about the variables, you can use whos instead which will return a struct that contains (among other things) the dimensions (size) and storage size (bytes).
As far as getting the value, you could use eval but this is not recommended and you should instead consider using cell arrays or structs with dynamic field names rather than dynamic variable names.
S = whos('*time*');
for k = 1:numel(S)
disp(S(k).name)
disp(S(k).bytes)
disp(S(k).size)
% The number of elements
len(k) = prod(S(k).size);
% You CAN get the value this way (not recommended)
value = eval(S(k).name);
end
#Suever nicely explained the straightforward way to get this information. As I noted in a comment, I suggest that you take a step back, and don't generate those dynamically named variables to begin with.
You can access structs dynamically, without having to resort to the slow and unsafe eval:
timestruc.field = time;
timestruc.('field1') = time_1;
fname = 'field2';
timestruc.(fname) = time_2;
The above three assignments are all valid for a struct, and so you can address the fields of a single data struct by generating the field strings dynamically. The only constraint is that field names have to be valid variable names, so the first character of the field has to be a letter.
But here's a quick way out of the trap you got yourself into: save your workspace (well, the relevant part) in a .mat file, and read it back in. You can do this in a way that will give you a struct with fields that are exactly your variable names:
time = 1;
time_1 = 2;
time_2 = rand(4);
save('tmp.mat','time*'); % or just save('tmp.mat')
S = load('tmp.mat');
afterwards S will be a struct, each field will correspond to a variable you saved into 'tmp.mat':
>> S
S =
time: 1
time_1: 2
time_2: [4x4 double]
An example writing variables from workspace to csv files:
clear;
% Writing variables of myfile.mat to csv files
load('myfile.mat');
allvars = who;
for i=1:length(allvars)
varname = strjoin(allvars(i));
evalstr = strcat('csvwrite(', char(39), varname, '.csv', char(39), ', ', varname, ')');
eval(evalstr);
end

Matlab map with multiple keys or nested

I am currently trying to map some data within a month depending on 'Month', 'week', 'day'.
This means that in principle I have three keys. I thought of following solutions to the problem:
1)
containers.Map(month,containers.Map(week,containers.Map(day,value)))
The problem with this solutions is to reference it I must use: How can I index a MATLAB array returned by a function without first assigning it to a local variable?.
And I find this solution rather "ugly".
2)
So another way would be to use a Map with multiple keys. The containers documentation show that only a single dimension key is allowed.
Question:
Do you have any trick to solve this multiple key problem?
Update/Solution:
I ended using a concatenated string as a key (as suggested) I added following piece of code to make the containers.Map as a proper multi-dimensional hashmap. Take a look below (I excluded week for simplification):
classdef example
properties
myMap % Map for storage
end
methods
function obj = example()
obj.myMap = containers.Map;
end
function obj2 = setVal(obj2,value,Month,DayType)
key = strcat(num2str(Month),'-',num2str(DayType));
obj2.myMap(key) = value;
end
function value = getValue(obj,Month,DayType)
key = strcat(num2str(Month),'-',num2str(DayType));
value = obj.myMap(key);
end
end
You could perhaps use all three of those keys to build a single key. I'm assuming this can be done as Month, week and day could be considered as unique. There is only one unique combination of these per occurrence. As such, simply take these keys and build them into single strings, then use theses as keys into your dictionary / containers.Map().
Here's an example:
%// Test data
month1 = 'May';
week1 = 2;
day1 = 'Thursday';
month2 = 'June';
week2 = 3;
day2 = 'Friday';
month3 = 'July';
week3 = 4;
day3 = 'Sunday';
%// Define keys
key1 = [month1 num2str(week1) day1];
key2 = [month2 num2str(week2) day2];
key3 = [month3 num2str(week3) day3];
%// Build dictionary
M = containers.Map();
M(key1) = 'Hello!';
M(key2) = 'Testing!';
M(key3) = 'Yes!';
%// Now test accessing
disp(M(key1));
disp(M(key2));
disp(M(key3));
The above code will take three months, three weeks and days, convert them into strings, and use these as keys into your dictionary. I don't know what the output type is for your purposes, so I just assigned strings. Note that I took the numbers and used num2str to convert the numbers into strings to ensure compatibility with the rest of the string. I don't know what data type week is (or any of the other variables in fact...), so simply use what I have and modify it for your own purposes.
I create the dictionary, then to test it, I access each of the values with each of the keys. As expected, my output is:
Hello!
Testing!
Yes!
There is a new MapNested class on Matlabs File-exchange:
http://de.mathworks.com/matlabcentral/fileexchange/62492-mapnested-implementation-for-nested-maps--map-of-maps-
or on github:
https://github.com/RolandRitt/Matlab-NestedMap
Check it out!
The syntax for setting an value is the following:
NMapobj = MapNested(); %constructor;
NMapobj(key1, key2, key3) = value;
for retrieving:
value = NMapobj(key1, key2, key3);

Access data in structures when field names are unknown

I have data as a struct with several layers, for example:
data.A.B
The data I want to access is in layer B. But the problem is that field names in B can be different depending on where the data comes from. Therefore I can't just type:
data.A.B.myData
myData is itself a struct
I can use:
fieldnames(data.A)
to find the names, but this doesn't help my much. I would have to write code sections for every possible field name that can occur at this level. And that's just what i trying to avoid.
Is there a way to get down to the data I have (myData) without knowing the field names of B?
Traditionally, you can loop over the fieldnames and perform the search of myData at a specific sub-structure of the struct. However, if you don't know which sub-structure you need to search, then you can perform a recursive algorithm. Below is an example. It will return the first match of myData in the struct or an empty matrix if no match found. The code can be improved to find all matches of myData.
function S2=getmyfield(S1,queriedField)
if isstruct(S1)
% Get all fieldnames of S1
fieldArray=fieldnames(S1);
% Find any match with the queried field. You can also use isfield().
% If there is a match return the value of S1.(queriedField),
% else perform a loop and recurse this function.
matchTF=strcmp(queriedField,fieldArray);
if any(matchTF)
S2=S1.(fieldArray{matchTF});
return;
else
S2=[];
i=0; % an iterator count
while isempty(S2)
i=i+1;
S2=getmyfield(S1.(fieldArray{i}),queriedField);
end
end
else
S2=[];
end
end
Cheers.
You just need a recursive function that checks fieldnames at each level for the structure.
This is roughly what you need (it could be improved to supply the path to the found field).
function [ value, found ] = FindField( rootStruct, fieldName )
%FindField - Find a field with a structure
value = [];
found = 0;
if isstruct( rootStruct )
fields = fieldnames(rootStruct);
for fi=1:length(fields)
if strcmp(fields{fi}, fieldName )
value = rootStruct.(fieldName);
found = true;
return;
end
[value, found ] = FindField( rootStruct.(fields{fi}), fieldName );
if found
return;
end
end
end
end
Usage example:
a.b = 1;
a.b.c = 2;
a.b.d = struct('Index',1,'Special',2);
FindField(a,'d')
ans =
Index: 1
Special: 2

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))'])