Consider the following example:
x = magic(3);
figure(1); clf(1);
plot( x, '-r', 'DisplayName', 'Magic' );
legend( 'show' );
The resulting legend entries in MATLAB R2014a are
getcolumn(Magic,1)
getcolumn(Magic,2)
getcolumn(Magic,3)
The problem stems from function [leg,labelhandles,outH,outM] = legend(varargin) in legend.m (Copyright 1984-2012 The MathWorks, Inc.), line 628:
str{k} = get(ch(k),'DisplayName');
More specifically, the function get
prepends getcolumn( and
appends , <Column Number>).
Is there an easy way to display exactly one legend entry (or multiple, but without the pre- and appended strings) for multiple data rows named after DisplayName, which have the same visual properties?
An alternative would of course be to programatically create multiple (or one) legend entries through plot handles (see below), but I would like to keep things short and simple.
One entry:
x = magic(3);
figure(1); clf(1);
h = plot( x, '-r' );
legend( h(1), 'Magic' );
Multiple entries:
x = magic(3);
figure(1); clf(1);
h = plot( x, '-r' );
strL = cell( 1, numel(h) );
for k = 1:numel(h)
strL{k} = sprintf( 'Magic %d', k );
end
legend( h, strL );
In MATLAB R2014b, the problem with getcolumn(Name,Row) does not appear anymore for the first code example.
If you want to set multiple display names for the legend entries in short syntax, you just need to prepare a cell array with them, let's say it's called leg_names, and then use set to apply them to all at once:
set(p,{'DisplayName'},leg_names);
Let's take a look at an example:
x = magic(3); % your data
p = plot( x,'-r'); % plot and get an array of handles to the lines
% create a list of the desired names for the legend entries:
leg_names = [repmat('Magic ',size(x,2),1) num2str((1:size(x,2)).')];
set(p,{'DisplayName'},cellstr(leg_names)); % set all display names
legend('show'); % show the legend
The result is exactly as in your examples at the end of the question.
Also, note that the syntax: [lgd,icons,plots,txt] = legend(___) is not recommended (from the docs):
Note: This syntax is not recommended. It creates a legend that does not support all graphics features. Instead, use the lgd = legend(__) syntax to return the Legend object and set Legend Properties.
Related
I currently want to have the legend of graph, however i'm plotting several lines that should be group in only 3 types.
My currently option is to use a dummy plot out of the boundaries, plotting the relevant data and calling the legend just at the end. It works but it is prone to errors. I wanted to update the legend and select just a few of the plots.
I tried to use the leg_handle.String, but then it comes two problems:
It still plot 5 handles instead of 3.
It does not have the proper line style & color.
Any ideas?
Bellow follow the code (with dummy plot commented) and the pictures of the current version giving the error and what i want to look.
clear
figure()
hold on
%using
%dummy plot
% leg_text={'a','b','c'};
% plot(100,100,'-r')
% plot(100,100,'-b')
% plot(100,100,'-k')
for ii=1:20,
plot(1:11,linspace(0,ii,11),'-r')
end
for ii=30:50,
plot(1:11,linspace(0,ii,11),'-b')
end
for ii=70:80,
plot(1:11,linspace(ii,25,11),'-k')
end
Yaxl=[-1 80];
Xaxl=[1 11];
set(gca, 'Visible','on', ...
'Box','on', ...
'Layer','top',...
'Xlim',Xaxl, ...
'Ylim',Yaxl);
%using
% legend(leg_text)
%want to use
leg_hand=legend(gca,'show');
leg_hand.String=leg_hand.String([1 21 42]);
%extra comand will give the things that i wanted above
% leg_hand.String=leg_hand.String([1 2 3]);
What it gives:
What I expect to have:
I have tried this method using [a,b,c,d]=legend, but this give only the a handle that i already using.
This little workaround should do the job:
clear();
figure();
hold on;
h = gobjects(3,1);
for ii = 1:20
h(1) = plot(1:11,linspace(0,ii,11),'-r');
end
for ii = 30:50
h(2) = plot(1:11,linspace(0,ii,11),'-b');
end
for ii = 70:80
h(3) = plot(1:11,linspace(ii,25,11),'-k');
end
set(gca,'Box','on','Layer','top','Visible','on','Xlim',[1 11],'Ylim',[-1 80]);
legend(h,'A','B','C');
hold off;
Actually, what I did is very simple. I created an array of graphical objects of size 3 (one for each iteration) using the gobjects function. Then, inside each iteration, I assigned the last plotted line to its respective array placeholder. Finally, I created the legend using the three graphical objects I previously stored.
Alternatively:
clear();
figure();
hold on;
h1 = gobjects(20,1);
for ii = 1:20
h1(ii) = plot(1:11,linspace(0,ii,11),'-r');
end
h2 = gobjects(21,1);
for ii = 30:50
h2(ii-29) = plot(1:11,linspace(0,ii,11),'-b');
end
h3 = gobjects(11,1);
for ii = 70:80
h3(ii-69) = plot(1:11,linspace(ii,25,11),'-k');
end
set(gca,'Box','on','Layer','top','Visible','on','Xlim',[1 11],'Ylim',[-1 80]);
legend([h1(1) h2(1) h3(1)],'A','B','C');
hold off;
You create an array of graphical objects for storing the plot handlers produced by every iteration. Then you create the legend using the first (basically, any) item of each array of graphical objects.
I need to prevent a specific plot entry from being displayed on a Matlab plot legend.
Sample:
% x and y are any plot data
for i=1:5
plot(x,y);
plot(x2,y2,'PleaseNoLegend!'); % I need to hide this from legend
end
legend('show');
Is there any flag I can set inside the plot command so this specific entry doesn't show up in legend?
You can achieve that by setting the 'HandleVisibility' property to 'off'. Note that this hides the handles of those plots to all functions, not just to legend.
For example,
hold on
for k = 1:3
x = 1:10;
y = rand(1,10);
x2 = x;
y2 = y + 2;
plot(x,y);
plot(x2,y2,'--','HandleVisibility','off'); % Hide from legend
end
legend('show')
produces the graph
You can use the semi-documented function called hasbehavior, that allows you to ignore individual plots in a legend after you issued the plot command.
figure;
hold on;
for i=1:5
plot(x,y);
h = plot(x2,y2);
hasbehavior(h,'legend',false);
end
legend('show');
The fact that it's semi-documented suggests that it could break sooner or later in a newer MATLAB version, so use with care. It might still be a convenient choice for certain applications.
As #stephematician noted, this MATLAB built-in is also unavailable in Octave, which might be another reason why the other answers are preferable.
As Luis Mendo mentions (and I somehow missed this) the handle is hidden to all other functions in his answer, which will be ok in most situations, but an alternative solution which looks identical to the above and doesn't have this effect is:
k_values = 1:3;
h = nan(size(k_values));
x = 1:10;
hold on
for k = k_values
y = rand(size(x));
y2 = y + 2;
h(k) = plot(x,y);
plot(x,y2,'--');
end
hold off
legend(h, strcat('data', num2str(k_values')))
The final command sets the legend entry for each handle returned by the plot(x,y) command. The first argument is a 1x3 array of line handles which will appear in the legend, and the second argument is a 3x5 char matrix where each row is a label.
I was expecting that to work, but I am missing what is a "vector of handles", from MATLAB helpfile.
LEGEND(M), where M is a string matrix or cell array of strings, and
LEGEND(H,M) where H is a vector of handles to lines and patches also
works.
myone = ones(20,1);
mytwo = ones(20,1)+1;
rows = vertcat(myone,mytwo);
mylabels = {'Alpha', 'Beta'};
figure
grouplabels = mylabels(rows);
h = scatter3(rand(40,1),rand(40,1),rand(40,1),20,rows,'filled'), ...
view(-33,22)
legend(handle(h),grouplabels)
xlabel('X')
ylabel('Y')
zlabel('Z')
The problem with your code is that h, the output of scatter3, is a single handle. It's not an array of handles with the same size as your data (which is what you imply when trying to set 40x1 array of labels on it, ignoring irrelevant handle wrapper). And it's not even an array of two handles as one may have thought (one per color). So you cannot set legend like this. One way around would be to plot all the points of one color at a time:
hFig = figure();
axh = axes('Parent', hFig);
hold(axh, 'all');
h1 = scatter3(rand(20,1),rand(20,1),rand(20,1),20,'b','filled');
h2 = scatter3(rand(20,1),rand(20,1),rand(20,1),20,'r','filled');
view(axh, -33, 22);
grid(axh, 'on');
legend(axh, [h1,h2], {'Alpha', 'Beta'});
I need to produce a X vs Y graph and make a distinction between positive class and negative class (indicated in original data infile). How do I produce a legend in such a graph? This is my code for the graph right now :
infile = fopen('ClassData1.txt','r');
data = textscan(infile,'%f %f %f');
parameters = [data{1} data{2}];
label = [data{3}];
h = ones(100,9);
g = ones(100,9);
score1= ones(1,9);
sc = 0;
figure
for i = 1:100
if label(i)>0
plot(parameters(i,1),parameters(i,2),'r*')
hold on
else
plot(parameters(i,1),parameters(i,2),'b*')
end
end
To show each line type only once, you have to keep the handles. Storing only one handle per class is sufficient.
h=nan(2,1)
for i = 1:100
if label(i)>0
h(1)=plot(parameters(i,1),parameters(i,2),'r*')
hold on
else
h(2)=plot(parameters(i,1),parameters(i,2),'b*')
end
end
legend(h)
There are (more than) two ways to do this. Firstly, you can just use a single plot command and do the legend normally, by combining the plot definitions using logical indexing, or you can use the DisplayName property for each plot to give information about the legend.
% Some sample data
parameters=rand(100,2);
label=parameters(:,1)-0.5;
% Use logical indexing to replace the for loop and use a single plot command
figure(1)
plot(parameters(label>0,1),parameters(label>0,2),'r*',parameters(label<=0,1),parameters(label<=0,2),'b*')
legend('red','blue')
% Use DisplayName to set the legend entry for each plot, then show the legend using the names given
figure(2)
plot(parameters(label>0,1),parameters(label>0,2),'r*','DisplayName','Red')
hold on
plot(parameters(label<=0,1),parameters(label<=0,2),'b*','DisplayName','Blue')
hold off
legend('show')
I have 13 lines on a plot, each line corresponding to a set of data from a text file. I'd like to label each line starting with the first set of data as 1.2, then subsequently 1.25, 1.30, to 1.80, etc., with each increment be 0.05. If I were to type it out manually, it would be
legend('1.20','1.25','1.30', ...., '1.80')
However, in the future, I might have more than 20 lines on the graph. So typing out each one is unrealistic. I tried creating a loop in the legend and it doesn't work.
How can I do this in a practical way?
N_FILES=13 ;
N_FRAMES=2999 ;
a=1.20 ;b=0.05 ;
phi_matrix = zeros(N_FILES,N_FRAMES) ;
for i=1:N_FILES
eta=a + (i-1)*b ;
fname=sprintf('phi_per_timestep_eta=%3.2f.txt', eta) ;
phi_matrix(i,:)=load(fname);
end
figure(1);
x=linspace(1,N_FRAMES,N_FRAMES) ;
plot(x,phi_matrix) ;
Need help here:
legend(a+0*b,a+1*b,a+2*b, ...., a+N_FILES*b)
As an alternative to constructing the legend, you can also set the DisplayName property of a line so that the legend is automatically correct.
Thus, you could do the following:
N_FILES = 13;
N_FRAMES = 2999;
a = 1.20; b = 0.05;
% # create colormap (look for distinguishable_colors on the File Exchange)
% # as an alternative to jet
cmap = jet(N_FILES);
x = linspace(1,N_FRAMES,N_FRAMES);
figure(1)
hold on % # make sure new plots aren't overwriting old ones
for i = 1:N_FILES
eta = a + (i-1)*b ;
fname = sprintf('phi_per_timestep_eta=%3.2f.txt', eta);
y = load(fname);
%# plot the line, choosing the right color and setting the displayName
plot(x,y,'Color',cmap(i,:),'DisplayName',sprintf('%3.2f',eta));
end
% # turn on the legend. It automatically has the right names for the curves
legend
Use 'DisplayName' as a plot() property, and call your legend as
legend('-DynamicLegend');
My code looks like this:
x = 0:h:xmax; % get an array of x-values
y = someFunction; % function
plot(x,y, 'DisplayName', 'Function plot 1'); % plot with 'DisplayName' property
legend('-DynamicLegend',2); % '-DynamicLegend' legend
source: http://undocumentedmatlab.com/blog/legend-semi-documented-feature/
legend can also take a cell list of strings as an argument. Try this:
legend_fcn = #(n)sprintf('%0.2f',a+b*n);
legend(cellfun(legend_fcn, num2cell(0:N_FILES) , 'UniformOutput', false));
The simplest approach would probably be to create a column vector of the numbers to use as your labels, convert them to a formatted character array with N_FILES rows using the function NUM2STR, then pass this as a single argument to LEGEND:
legend(num2str(a+b.*(0:N_FILES-1).','%.2f'));
I found this I found through Google:
legend(string_matrix) adds a legend containing the rows of the matrix string_matrix as labels. This is the same as legend(string_matrix(1,:),string_matrix(2,:),...).
So basically, it looks like you can construct a matrix somehow to do this.
An example:
strmatrix = ['a';'b';'c';'d'];
x = linspace(0,10,11);
ya = x;
yb = x+1;
yc = x+2;
yd = x+3;
figure()
plot(x,ya,x,yb,x,yc,x,yd)
legend(strmatrix)