matlab adding single legend for multiple data - matlab

figure(1);
hold on;
na=4;
circle_X = [0 0 5 5]';
circle_Y = [0 3 0 3]';
for a = 1:na
r=0.3;
N=100;
theta=linspace(0, 2*pi, N);
cx=r*cos(theta)+circle_X(a);
cy=r*sin(theta)+circle_Y(a);
plot3(cx , cy, 300*ones(N), 'r', 'linewidth', 2,'DisplayName',sprintf('circle'));
end
legend('show');
I want to draw 4 circles and add a single legend 'circle' indicating 4 circles all at once, without using "legend('circle')".
For now the legend looks like this
how should I change the code?

First, you don't need plot3, you can achieve the same figure with plot, and probably the long legend is because of that.
just change the line of plot to that:
plot(cx , cy, 'r', 'linewidth', 2,'DisplayName','circle');
Now, the legend will have 4 entries because you draw four objects. If you want a single entry you have some ways:
add the legend inside the loop, after an if statement. For example,
if a==1 ,
legend('show');
end
get handles for your plots, and legend just one of them. It can be done directly from legend, but then you will need to specify the string:
for....
h(a)=plot...
end
legend(h(1),'circle')
get handles like in part 2, and if you don't want specify the string 'circle', you can use the undocumented hasbehavior:
for...
h(a)=plot...
end
hasbehavior(h(2),'legend',false);
hasbehavior(h(3),'legend',false);
hasbehavior(h(4),'legend',false);
l=legend('show');

Related

Making a legend when some of the data sets could be empty

For my project I have six sets of data to put on a scatter plot, like so:
plot(ax, ay, '.r', bx, by, '.b', cx, cy, '.m', dx, dy, '.c', ex, ey, '.y', fx, fy, '.k');
Sometimes these sets of data will be empty, so bx and by might have nothing in them, therefore getting skipped over.
Is there any way to build a legend that will match the right label to the right color piece of data? In other words, data in the [cx, cy] would always match the label 'c' on the legend next to a magenta dot, even when there is no 'b'. My current legend is as follows:
legend('a', 'b', 'c', 'd', 'e', 'f', -1);
Thanks!
You can get the results you want if you first replace any sets containing empty data with NaN values. For example:
x = [];
y = [];
if isempty(x) || isempty(y)
x = nan;
y = nan;
end
plot(1:10, rand(1,10), 'r', x, y, 'g', 1:10, rand(1,10), 'b');
legend('r', 'g', 'b');
Leaving x and y as empty would give a warning when creating the legend and result in an incorrect legend. When passing empty vectors ([]) to the plot command, it simply doesn't create a line object for that data. When passing a NaN value, it creates but doesn't render a line object, so it still exists and can have a legend entry generated for it.
To complete #gnovice answer with the advantage proposed in #Guto answer, if the order of the lines matters, you can still use NaN, and set their order after it:
x = 1:10;
% plot and get the handle to the lines:
p = plot(x,x,'--g',nan,nan,'r',x,x,'b','linewidth',2);
% set the order of the lines from front to back:
set(p(1).Parent,'Children',p(1).Parent.Children([3 2 1]))
% add the legend:
legend('data 1', 'data 2', 'data 3');
In the above example we bring the green line on top of the blue one:
One other possibility that does not use the x=NaN; is to use a 'dummy plot' out of the boundaries.
The drawback of this process is that you need to select the boundaries manually and if you made any changes in the main plots, you need to manually change the dummy plot as well. Bad idea for use in automated plots. Also If the legend is deleted and called again (either in the insert menu or by legend off; legend show)
The advantage is that you can plot it in a different order from the legend. This can be important when plotting multiple line types and thickness that overlay in some regions. In the figure below, for instance, if you plot the green first, it will disappear in the region x<5 under the blue line.
An example of code:
x_e = [];
y_e = [];
figure()
hold on
plot(100,100, '--g', 100,100, 'r', 100,100, 'b'); %dummy plot
x=1:10;
y=[1 3 3 3 3 4 5 6 9 10]/10;
y2=[1 3 3 3 3 7 6 6 4 3]/10;
plot(x,y2, 'b',x_e, y_e, 'r',x, y, '--g','linewidth',2);
set(gca,'box','on',... %box just to be prettier
'Xlim',[1 10], 'Ylim',[0 1]) % relevant set up!
legend('data 1', 'data 2', 'data 3');
and it give this graph:

Legend containing only specific plots

I have 10 curves in a plot, but only three of them should appear in the legend. For example, among 10 curves, just the first, 5th and 10th should be in the legend, how I can do this?
Here's my program:
x=1:0.5:15;
y1=x.^1
plot(x,y1)
hold on
y2=x.^1.2
plot(x,y2)
hold on
.
.
.
y10=x.^2.2
plot(x,y10)
You can use handles for the plots, and then specify the plots for the legend by their handles:
x=1:0.5:15;
y(1,:)=x.^1;
y(2,:)=x.^1.2;
...
...
...
y(10,:)=x.^2.2;
for k=1:10
h(k)=plot(x,y(k,:));
hold on
end
legend([h(1) h(5) h(10)],'curve 1','curve 5','curve 10');
hold off
legend can take a handle or handles, and a list of strings. I've taken the liberty of rewriting your code so it plots in a loop rather than creating a bunch of y variables. Generally speaking, if you find yourself creating a series of variables named y1, y2, etc, there's a better way of doing it in MATLAB.
There are 7 plots, not 10, but you get the idea.
x=1:0.5:15;
m=1:0.2:2.2;
figure
hold on
for n = 1:7
h(n) = plot(x,x.^m(n));
end
legend(h([1,3,5]),'Plot One', 'Plot Three', 'Plot Five',...
'Location', 'NorthWest')
you need to use the plot handles in the legend function to indicate the desired curves. Inserted in your code it would look like this:
x=1:0.5:15;
y1=x.^1;
h1=plot(x,y1,'r');
hold on
y2=x.^1.2;
h2=plot(x,y2,'c');
hold on
.
.
.
y10=x.^2.2;
h10=plot(x,y10,'p');
hold off;
legend([h2,h10] , 'Fart 2', 'More Fart'); % Plot in the handle you wish
Here's a slick two-liner that I use:
a=flipud(findall(gcf,'Type','Line')); %get all line objects of current plot
legend(a([1 3]),{'a','b'}) %add legend for line 1 and 3, 'a' and 'b'
The legend command only applies to the most recent created plot, unless a handle is passed.
figure
for c=1:10
subplot(4,4,c)
ezplot('y=sin(x)');
if c==5||c==10
legend('sin(x)')
end
end

Matlab - how to make a custom legend

I have the following picture :
And I would like to make a legend for it. Basically, I want to make a legend for each type of rectangle. In the legend box, I want to mark each color line according to the type of body which it marks:
green line : head
yellow line : torso
purple line : right arm
cyan line : left arm
red line : left leg
blue line : right leg
This is basically custom, because I have more rectangles of each type. How can I do a custom legend and attach it to the figure which draws this picture?
There are 2 ways you could go about this. You could create your squares and then assign them to an hggroup. This way you dont have multiple items for each color. Something like this:
hold on
for ii = 1:4
hb(ii) = plot(rand(1,2), rand(1,2),'color','r');
end
hg = hggroup;
set(hb,'Parent',hg)
set(hg,'Displayname','Legs')
legend(hg)
Or you could create dummy objects, like this:
hold on
for ii = 1:4
hb(ii) = plot(rand(1,2), rand(1,2),'color','r');
end
p = plot([],[],'r');
legend(p,'Legs')
The former is a little more elegant.
I would like to add to dvreed77's answer on using hggroup that for clean legend use, I also needed to set the 'IconDisplayStyle' (Matlab R2014a), such that:
%4 kinds of lines:
n_areas = 4;
n_lines = 10;
%use built-in color map
cmap = hsv(n_areas);
%plot lines and generate handle vectors
h_fig = figure;
hold on
h_lines = zeros(1,n_lines);
for l = 1:n_areas
for k = 1:n_lines
h_lines(k) = plot(rand(1,2), rand(1,2),'Color',cmap(l,:));
end
%Create hggroup and set 'icondistplaystyle' to on for legend
curPlotSet = hggroup;
set(h_lines,'Parent',curPlotSet);
set(get(get(curPlotSet,'Annotation'),'LegendInformation'),...
'IconDisplayStyle','on');
end
%Now manually define legend label
legend('heads','legs','hands','feet')
The simplest way I can think of is to first plot one rectangle of each type and construct a legend for only unique rectangles. Like so:
figure;
hold on;
% unique rectangles
plot(rand(1, 10), 'b');
plot(rand(1, 10), 'g');
% the rest
plot(rand(1, 10), 'b');
plot(rand(1, 10), 'g');
% use normal legend with only as many entries as there are unique rectangles
legend('Blue', 'Green');
You will have many lines of the same color, but a legend only for unique colors.
Just draw legend dots outside the plot:
figure;
plot(-1,-1,'gs',-1,-1,'b^',-1,-1,'ro');
legend('x1','x2','x3','Location','NorthWest');
xlim([0,1]); ylim([0,1]);
To control the appearance of legend entries, plot points that have values which are NaN then pass the objects returned by plot and an array of labels to the legend function (NaN points are not visible in the plot, but appear in the legend).
colors = ["red", "blue"];
labels = ["this is red", "this is blue"];
% We 'plot' a invisible dummy point (NaN values are not visible in plots),
% which provides the line and marker appearance for the corresponding legend entry.
p1 = plot(nan, nan, colors(1));
hold on
p2 = plot(nan, nan, colors(2));
% Plot the actual plots. You can change the order of the next two function calls
% without affecting the legend.
plot([0, 1], [0, 1], colors(1));
plot([0, 1], [1, 0], colors(2));
legend([p1, p2], labels)
If the plots in [p1, p2] are not in the current figure when legend([p1, p2], labels) is called, then it will raise the following error:
Invalid argument. Type 'help legend' for more information.
You can filter plots that are not in the current figure using something like this:
plots_in_figure = findall(gcf,'Type','Line');
plots_for_legend_indices = ismember([p1, p2], plots_in_figure);
plots_for_this_legend = this.plots_for_legend(plots_for_legend_indices);
legend(plots_for_this_legend, labels)

MATLAB graph plotting: assigning legend labels during plot

I am plotting data in a typical MATLAB scatterplot format. Ordinarily when plotting multiple datasets, I would use the command 'hold on;', and then plot each of the data, followed by this to get my legend:
legend('DataSet1', 'DataSet2') % etcetera
However, the (multiple) datasets I am plotting on the same axes are not necessarily the same datasets each time. I am plotting up to six different sets of data on the same axes, and there could be any combination of these shown (depending on what the user chooses to display). Obviously that would be a lot of elseif's if I wanted to setup the legend the traditional way.
What I really would like to do is assign each DataSet a name as it is plotted so that afterwards I can just call up a legend of all the data being shown.
...Or, any other solution to this problem that anyone can think of..?
You should be able to set the DisplayName property for each plot:
figure
hold on
plot(...,'DisplayName','DataSet1')
plot(...,'DisplayName','DataSet2')
legend(gca,'show')
http://www.mathworks.com/help/matlab/ref/line_props.html
Side Note: I've found a lot of little tricks like this by getting the figure to look the way I want, then choosing the Figure's "File" menu option "Generate M-File..." and inspecting the generated output code.
One option is to take advantage of the 'UserData' property like so:
figure;
hold on
plot([0 1], [1 0], '-b', 'userdata', 'blue line')
plot([1 0], [1 0], '--r', 'userdata', 'red dashes')
% legend(get(get(gca, 'children'), 'userdata')) % wrong
legend(get(gca, 'children'), get(get(gca, 'children'), 'userdata')) % correct
Edit: As the questioner pointed out, the original version could get out of order. To fix this, specify which handle goes with which label (in the fixed version, it is in the correct order).
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/
You can try something like the following
for k = 1:10
h(k) = plot(...);
name{k} = ['condition ' num2str(k)];
end
legend(h, name);
Make a for loop. But Before the for loop, make an array.
%for example
legendset = {}
for i = 1:10
%blabla
%Then in the fore loop say:
legendset = [legendset;namedata(i)]
%It puts all names in a column of legendset.
%Make sure namedata are characters.
%foreloop ends
end
%Then after the foreloop say:
legend(legendset).

stop matlab line plots from overlapping

I plot many lines on top of one another, using plot and hold on
however i want one of the lines to be shifted a bit if it falls on another line.
for example in the following case:
plot(1:100); hold on; plot(-100:100,abs(-100:100))
i want it to be clear that there are 2 plots here
i tried to simply increase x values for different plots but this skews the data too much
for z=1:numberofplots
plot((1:size(locations,2))+0.1*z,locations(z,:)','color', altclrz(z,:));
end
You can differentiate the curves in several ways:
-1- Skewing the data
As you said, you could shift the data a bit. I would suggest fixing your axis and then calculating how many units in linewidth is so you get a very tight fit, like this:
lineWidth = 5;
figure(33);
clf;
subplot(1,2,1);
h = plot(myData, 'linewidth', lineWidth);
xlim([1,5]);
ylim([1,5]);
title('Original');
myData = meshgrid(1:5)';
myLimDiff = diff(ylim);
set(gca,'units', 'pixels');
myPos = get(gca, 'position')
myWidthHeight= myPos(3:4)
PixelsPerUnit =myWidthHeight(2)./ myLimDiff;
myDataSkewed = myData + meshgrid(-2:2)*1/PixelsPerUnit(1)*lineWidth;
subplot(1,2,2);
plot(myDataSkewed, 'linewidth', lineWidth);
xlim([1,5]);
ylim([1,5]);
title('Skewed');
Result:
-2- Using solid lines and Dashes
As someone else noted in the comments, you could you a dashed line over a solid line, or some combination of styles.
-3- Using different line thickness
Use different line widths with the thickest on the bottom:
figure(54);
clf
hold all
for ind = 10:-3:1
plot(1:5, 'linewidth', ind);
end
-4- Use separate plots for each line with a twist
Another way to call out each line is to plot each line in a subplot but to plot all the data in gray first. This way you can see where all the lines are with each particular line called out:
figure(55);
clf
data = rand(3);
for ind = 1:3
subplot(1,3,ind);
plot(data, 'linewidth', 4, 'color', [1 1 1]*.75);
hold on
plot(data(:,ind), 'linewidth', 2);
end