Manipulating a structure for a more flexible plot

4 views (last 30 days)
Hi,
I am building an app to fit different models to experimental data, each corresponding to a given values of the variable Q, and at the end to plot the results for each parameters vs Q.
The fit output is a structure, named Int, with many fields.
For simplicity I consider here the case with only 3 Qs values:
Int =
struct with fields:
Q: [7 9 10]
ParsNames: {{13×1 cell} {13×1 cell} {13×1 cell}}
ParsFit: {[13×1 double] [13×1 double] [13×1 double]}
eParsFit: {[13×1 double] [13×1 double] [13×1 double]}
In this case the model has 13 parameters, that are stored for each Q in the cells ParsFit, with their errors, and names.
The ParsNames are always the same (Int.ParsNames{1}=Int.ParsNames{2}= ...), in this case:
Int.ParsNames{1} =
{'A0' }
{'A1' }
{'B0' }
{'B1' }
{'IL' }
{'GL' }
{'ID1' }
{'GD1' }
{'ED1' }
{'ID2' }
{'GD2' }
{'ED2' }
{'Chi2_r'}
I need to plot all the variables vs Q. For instance, for the 3 values of the parameter at the position 12:
for k = 1 : length(Int.Q)
ED2(k)=Int.ParsFit{1,k}(12,1); % I used the same variable name as the ParsNames
eED2(k)=Int.eParsFit{1,k}(12,1);
end
figure; errorbar(Int.Q,ED2,eED2)
This of course works, but I'd like to make the app more flexible because the number and name of parameters can vary.
In the example above, I named myself the variable with the proper name (ED2), and I’d avoid that: using another model, the parameter ED2 could be missing, and I would like to avoid writing different scripts for each case
As far as I understood, it is not suggested to define all the variables using dynamically the values contained in ParNames.
I wonder whether (and how) I can build another structure that is almost the transpose of Int with fields equal to those of Int:
newInt.A0 with all the Int.ParsFit{1,k}(1,1)
newInt.A1 with all Int.ParsFit{1,k}(2,1)
........
and put inside them all the Int.ParsFit{1,k}(12,1), and then plot all of them.
Is it possible to name the fields of the new structure with those of the original one Int.ParsNames{1} ?
Do you have suggestion to plot the data in the more clean, logical, and flexible way?
Thanks for your help!
  2 Comments
Stephen23
Stephen23 on 22 Mar 2022
Edited: Stephen23 on 22 Mar 2022
"Is it possible to name the fields of the new structure with those of the original one Int.ParsNames{1} ?"
Of course: you can get a cell array of all fieldnames using FIELDNAMES, and create fields is a structure either using SETFIELD or dynamic fieldnames (simpler):
In practice this will require a loop and paying careful attention to the details, but it isn't difficult.
"Do you have suggestion to plot the data in the more clean, logical, and flexible way?"
I would suggest that rather than a scalar structure (where every field has size 1xN) you might like to consider using a 1xN non-scalar structure, which means you don't need those cell arrays any more.
"I need to plot all the variables vs Q.... but I'd like to make the app more flexible because the number and name of parameters can vary."
Of course they can. There is absolutely no need for the variable you use in the code to have the same name as your meta-data. If you used a non-scalar structure then you could trivially concatenate them into matrices too (rather than lots of anti-pattern separate variables with ugly-numbered variable names), which would make plotting easier because you could use the inbuilt ability to plot matrices rather than trying to mess around with lots of separate variables and whatnot.

Sign in to comment.

Accepted Answer

Stephen23
Stephen23 on 22 Mar 2022
Edited: Stephen23 on 23 Mar 2022
Data in a 3x1 structure array (rather than a scalar structure with nested cell arrays as you showed) means slightly simpler data access because you can use a simpler syntax for accessing the field data in comma-separated lists.
C = {'A0';'A1';'B0';'B1';'IL';'GL';'ID1';'GD1';'ED1';'ID2';'GD2';'ED2';'Chi2_r'};
S(1).Q = 7;
S(1).ParsNames = C;
S(1).ParsFit = rand(13,1);
S(1).eParsFit = rand(13,1);
S(2).Q = 9;
S(2).ParsNames = C;
S(2).ParsFit = rand(13,1);
S(2).eParsFit = rand(13,1);
S(3).Q = 10;
S(3).ParsNames = C;
S(3).ParsFit = rand(13,1);
S(3).eParsFit = rand(13,1);
Plot:
Q = [S.Q];
M = [S.ParsFit];
ax = axes();
ax.LineStyleOrder = {'-o','-+','-*'};
hold(ax,'on')
plot(ax,Q,M.')
legend(S(1).ParsNames)
  6 Comments
Ferdi
Ferdi on 24 Mar 2022
Edited: Ferdi on 24 Mar 2022
Stephen, I wanted to let you know that I think to have solved my problem. I tried to make more flexible the choice of the pars name pfx = {'A','B', 'I', 'G', 'E', 'Ch'}; because their name can slightly vary in the various fits.
So, I am fine and you can stop to read the message here.
However, I append below the main sequence of what I am doing in case you like to have a look.
I will probably come back with other questions as I have still a long route in fornt of me.
Many thanks for your help.
%%%%%%%%%%
I have experimental data named I1D_L180_1A10, where the bolded strings may vary. The Q used in the following is the last number: Q=10 in this case.
My main app makes calls for some subset of data, for instance I1D_L180_1AQ, Q being an integer number (having a physical meaning).
I fix a given Q and make the fit. I store the data in the main app in a structure named app.results
%%
app.results =
struct with fields:
Q: 9
ParsNames: {13×1 cell}
ParsFit: [13×1 double]
eParsFit: [13×1 double]
Ifx: [100×1 double]
Ex: [100×1 double]
Ix: [100×1 double]
err_Ix: [100×1 double]
parRes: [1.3711 -0.0074 9.2646e-05]
Resx: [100×1 double]
Resd: [127×1 double]
T: 180
opt: [1×1 struct]
LB: [12×1 double]
UB: [12×1 double]
fix: [12×1 logical]
Ed: [127×1 double]
Ifd: [127×1 double]
YMnoC: [127×1 double]
YF_noC: [127×1 double]
YL: [127×1 double]
YD1: [127×1 double]
YD2: [127×1 double]
If I am happy with the fit I export the data, at a fixed Q, in a session .mat
Iall=app.results;
name = ['Res_' [app.SpectraName] '.mat'];
[fnm,pth] = uiputfile(name);
if ischar(fnm)
save(fullfile(pth,fnm),'Iall');
end
Now, we are at the core of my original question : I am writing another callback function where plotting (and making other stuff) with the outputs of the fit.
After your suggestions I ended up with the following :
data_folder = uigetdir(); %
if data_folder == 0
return;
end
% the good mat sessions
file_pth = fullfile(data_folder, 'Res*.mat');
filen = dir(file_pth);
% load data and get rid of the common field Iall
lQ=length(filen);
for k = 1 : lQ;
fname0 = filen(k).name;
fname = fullfile(filen(k).folder, fname0);
fprintf(1, 'Now reading %s\n', fname);
A=load(filen(k).name);
Int(k)=A.Iall;
end
% take out Qs, pars with errors
Q = [Int.Q]';
Mtest = [Int.ParsFit];
eMtest = [Int.eParsFit];
% regrouping pars according to their first letter
C=Int.ParsNames;
% where C'= {'A0'} {'A1'} {'B0'} {'B1'} {'IL'} {'GL'} {'ID1'} {'GD1'} {'ED1'} {'ID2'} {'GD2'} {'ED2'} {'Chi2_r'}
C1=extract(string(C),1);
[uC1,~,indC1] = unique(C1(:));
pfx = cellstr(uC1);
that gives the same result than pfx = {'A','B', 'I', 'G', 'E', 'Ch'};
% plot
for k = 1:numel(uC1)
ind = find(startsWith(C,pfx{k}));
fgh = figure(k);
axh = axes(fgh);
Qind=repmat(Q(:),1,numel(ind)); % thanks
errorbar(axh,Qind,Mtest(ind,:)',eMtest(ind,:)','o-');
xlim([0, max(Q)+1]);
legend(C(ind));
end
% some test data
Mtest =
0.0414 0.3010 0.0861
0.9754 0.9864 0.9892
0.0004 0.0005 0.0005
0.0000 0.0000 0.0000
0.0014 0.0001 0.0000
0.0700 0.0959 0.1527
0.0371 0.0206 0.0360
0.1048 0.1000 0.1271
5.5061 5.2542 4.4344
0.0424 0.0068 0.0330
0.7690 2.8483 1.2135
9.9690 11.0839 7.4763
3.8566 3.5473 3.3208
eMtest =
0.0097 0.0107 0.0167
0.0000 0.0000 0.0000
0.0000 0.0000 0.0000
0.0000 0.0000 0.0000
0.0003 0.0000 0.0000
0.0000 0.0000 0.0001
0.0087 0.0007 0.0070
0.0000 0.0000 0.0000
0.0000 0.0000 0.0000
0.0099 0.0002 0.0064
0.0000 0.0000 0.0000
0.0000 0.0000 0.0000
0 0 0
Stephen23
Stephen23 on 24 Mar 2022
"However, I append below the main sequence of what I am doing in case you like to have a look."
Good work, it looks fine to me.
The main thing is, that you can see ways to use a loop to process your data groups.

Sign in to comment.

More Answers (0)

Categories

Find more on Environment and Settings in Help Center and File Exchange

Products


Release

R2021b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!