How do we plot repetitive items simply? Can a loop and eval be used, for example? How d'you nest a string within a string?

2 visualizaciones (últimos 30 días)
Note the repetitive nature of the following script. Isn't it possible code this more simply using a loop of some kind, and perhaps the 'eval' command? Especially if you wanted to plot more than six items without changing the code? The 'co60', '4', '6','10','15', etc. correspond to the names of the data files (co60.dat, 4.dat, etc); I was able to create these vectors via the eval command. One difficulty I face is that I seem unable to nest strings within strings: For example, how do you specify 'o' when eval requires string inputs? How do you put a string within a string?
% This plots all 18 lines (3 per energy spectra) on one graph.
%figure
% hold on
% plot(depth,sk_ratio1,'.',depth,sk_ratio2,'o',depth,sk_ratio3,'x', ...
% depth,sk_ratio4,'+',depth,sk_ratio5,'*',depth,sk_ratio6,'s')
% plot(depth,fitco60_1_plot,'-',depth,fit4_1_plot,'-', ...
% depth,fit6_1_plot,'-', depth,fit10_1_plot,'-', ...
% depth,fit15_1_plot,'-', depth,fit24_1_plot,'-')
% plot(depth,fitco60_2_plot,'--',depth,fit4_2_plot,'--', ...
% depth,fit6_2_plot,'--', depth,fit10_2_plot,'--', ...
% depth,fit15_2_plot,'--', depth,fit24_2_plot,'--')
% title(['S/K vs Depth'])
% xlabel(['Depth in cm'])
% ylabel('Scerma to Kerma Ratio for Various Energy Spectra')
% legend('Co-60','4 MV','6 MV','10 MV','15 MV','24 MV')
% This plots the lines for each energy spectra in its own graph in figure.
figure
subplot(2,3,1)
plot(depth,sk_ratio1,'.',depth,fitco60_1_plot,'-',depth,fitco60_2_plot,'--')
xlabel('a')
legend('Co-60','Linear fit','Quadratic fit')
subplot(2,3,2)
plot(depth,sk_ratio2,'o',depth,fit4_1_plot,'-',depth,fit4_2_plot,'--')
xlabel('b')
legend('4 MV','Linear fit','Quadratic fit')
subplot(2,3,3)
plot(depth,sk_ratio3,'x',depth,fit6_1_plot,'-',depth,fit6_2_plot,'--')
xlabel('c')
legend('6 MV','Linear fit','Quadratic fit')
subplot(2,3,4)
plot(depth,sk_ratio4,'+',depth,fit10_1_plot,'-',depth,fit10_2_plot,'--')
xlabel('d')
legend('10 MV','Linear fit','Quadratic fit')
subplot(2,3,5)
plot(depth,sk_ratio5,'*',depth,fit15_1_plot,'-',depth,fit15_2_plot,'--')
xlabel('e')
legend('15 MV','Linear fit','Quadratic fit')
subplot(2,3,6)
plot(depth,sk_ratio6,'s',depth,fit24_1_plot,'-',depth,fit24_2_plot,'--')
xlabel('f')
legend('24 MV','Linear fit','Quadratic fit')
I ask basically the same questions below, but in perhaps a more confusing or verbose way. ~~~
Two problems:
  1. How to plot multiple things without manually typing: PLOT(X1,Y1,S1,X2,Y2,S2,X3,Y3,S3,...)
  2. How to create a legend for multiple things without manually typing: LEGEND(string1,string2,string3, ...)
I'm trying to plot six things and describe each uniquely, but possibly more than six (I'm trying to write the code so it's flexible, should someone want to use it to plot twenty things, for example). I thought I could simply repeat the plot and legend command within a for loop and it would automatically know what I'm trying to do, but it seems that doesn't happen (the lines are all the same color and the legend command overwrites the previous legend with each execution):
figure
hold on
for i = 1:numberofspectra
spectrumname = input('Please input the spectrum''s file name: ','s');
% sk_ratio is calculated; by the way, the spectrum file ends in .dat
eval(['sk_ratio' num2str(i) '= s_depth./k_depth;'])
dotpos = find(spectrumname == '.', 1, 'last'); % from Walter Roberson
if ~isempty(dotpos); spectrumname(dotpos:end) = []; end % Walter Roberson
eval(['plot(depth,sk_ratio' num2str(i) ')'])
legend(spectrumname)
end
title(['S/K vs Depth'])
xlabel(['Depth in cm'])
ylabel('Scerma to Kerma Ratio for Various Energy Spectra')
It would be great to be able to plot the lines distinctly, e.g. long and short dashes, solid, dotted, etc, and it might be necessary, but I think the minimum is to use colored lines (the MATLAB default). I see that one would use PLOT(X,Y,S) in the case of individual plots, but I don't know whether the syntax changes if trying to do what I'm describing here.
So is it even possible? Or is there no way around it? I've tried the following (although not very useful since I don't know how to tell it to omit the final comma after the last pair), which fails as shown (and as expected):
x1 = rand(1,3);
y1 = rand(1,3);
x2 = rand(1,3);
y2 = rand(1,3);
x3 = rand(1,3);
y3 = rand(1,3);
x4 = rand(1,3);
y4 = rand(1,3);
figure
plot( ...
for i = 1:3
eval(['x' num2str(i) 'y' num2str(i) ','])
end
x4,y4)
??? for i = 1:3
|
Error: Illegal use of reserved keyword "for".
So how do you automate a multiple plot+legend, particularly with unique lines and well-placed legend (if possible)? Thank you!
  1 comentario
Oleg Komarov
Oleg Komarov el 19 de Feb. de 2012
I am reading your post, it will take some time, but I am already sure that I will suggest you to avoid eval and to use structures with dynamic field indexing.

Iniciar sesión para comentar.

Respuesta aceptada

Oleg Komarov
Oleg Komarov el 19 de Feb. de 2012
First you have to forget about eval. There are very few things that can't be done without it (like swapping variables within different workspaces) but this is definitely not it.
eval is prone to errors, complicates readability, hard to debug, overpopulates the workspace, makes project management unfeasible.
I will answer directly your questions, but your issue is more broad and it concerns more project management rather than how to do it in MATLAB.
Question 1
in = {1:10, 11:20, '.',3:4,2:3,'*'};
plot(in{:})
Question 2
str = {'A','B'};
legend(str{:})
What I want to show here is one simple concept. plot and legend have varargin behaviour. They can accept a variable amount of inputs, the trick is to throw those inputs sequentially by "unpacking" the cell array (try to run in{:} and see what happens).
Having shown you one of the methods I would not recomend to follow it blindly. For instance, you "subplot" 3 series, sk_ratio, linear and quadratic fit, but the depth is the same. I would go for a double loop, one for each different group of results and one across the 3 series.
Project management
Obviously you cannot simply import all of your data in a cell array and throw results in a casual way, or hardcode everything as I did to simplify the example.
However, you can use cell arrays in a smarter way or you could use structure which have the benefit of keeping names for the fields (making it easier to remember what corresponds to what).
When I code for myself I use cell arrays and I use columns to separate groups of results and rows to store each specific result. An example:
{sk_ratio01, sk_ratio02;
fitco_1_01, fitco_1_02;
fitco_2_01, fitco_2_02;
'Co-60' , 'MV 4';
'*' , '+'}
where the names for the data are there to ease understanding but in reality I would not create sk_ratio01,...0n but just assign the data to the right position in the cell.
If I have to code for others as well I would use a structure:
s.('sk_ratio01') = struct('data',data,'fit1',fit1,'fit2',fit2,'marker','*','lbl','Co-60');
s.('sk_ratio02') = struct('data',data,'fit1',fit1,'fit2',fit2,'marker','+','lbl','MV 4');
etc..
Summary
To summarize my suggestion: managing correctly your data is the first thing you should aim at. There are different ways of doing it (and eval is not one of them) which involve trade-off between clarity, ease of use and flexibility of the solution.
Both methods I've shown you above (cell arrays and structures) could be implemented programmatically.
EDIT to comment
eval(['spectrum' spectrumname '.energy=' spectrumname])
could be rewritten with dynamic field indexing:
spectrum.(['spn' spectrumname]).energy = spectrumname;
Note that a fieldname cannot start with a number (same rule as for variables applies).
  3 comentarios
Daniel
Daniel el 19 de Feb. de 2012
I am (obviously) quite a beginner. I have been introduced to structures by Rudra Pratap's book _Getting Started with MATLAB 7_; thank you for showing me an application of them, Komarov. I would like to rewrite my code accordingly, if I have time. Currently, time is an issue, and I am trying to finish as quickly as possible; this combined with my inexperience I suppose explains what is apparently horrendous programming ... I recognize the value of 'doing it right the first time'; I had thought I was being clever in using eval, since I've established a loop to input data sequentially and then it spit out the results I want: Certainly that's better than hardcoding for each data file separately ... (Perhaps not much better, though, if eval is so bad.) Thanks!
Oleg Komarov
Oleg Komarov el 20 de Feb. de 2012
Don't worry, I used eval extensively in my first year of programming with MATLAB and it took me some time to realize that I usually don't need it.
See edit above for answer to your second comment.

Iniciar sesión para comentar.

Más respuestas (0)

Categorías

Más información sobre Function Creation en Help Center y File Exchange.

Productos

Community Treasure Hunt

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

Start Hunting!

Translated by