I'm trying to reverse my legend entries order based on reverse ordering of legend colors in matlab bar plot, but it doesn't seem to work in my case.
Basicaly what I have is a GUIDE figure, that draws a lot of plots and is able to save them to a .png file. The effect looks like this:
I've managed to change the text order by flipping legend upside-down, but I can't change legend colors order.
Here's what I've got:
[a b] = legend(legenda);
map = colormap; % current colormap
n = size(b,1);
z = linspace(size(map,1),1,n/3); % there is 1 text and 2 line elements for every data series, so I divide by 3
z = round(z); %otherwise matlab gets angry that indices must be real integers or logicals
MAP = map(z(:),:); % gets elements specified by linspace from colormap
So far everything works ok.
The b vector for two series looks like this (starts with 2.0, because it's reversed):
Text (P+C 200 2.0.dpt)
Text (P+C 200 1.0.dpt)
Line (P+C 200 2.0.dpt)
Line (P+C 200 2.0.dpt)
Line (P+C 200 1.0.dpt)
Line (P+C 200 1.0.dpt)
So I figured out (based on the linked code), I have to change the color variable for every line entry.
for k = (n/3 + 1):n
a1 = get(b(k),'Children');
set(a1,'FaceColor',MAP(ceil((k - n/3)/2), :));
end
Ceil and dividing by 2 gives the same index twice.
This code, however, does nothing.
I've checked whether flipping the legend vector may be the source of my problems, but the color order stays the same. I've tried with MAP vector too - no luck.
When I remove the semicolon after the a1 = ... line in the for loop, I get:
a1 =
0x0 empty GraphicsPlaceholder array.
How can I get this to work?
Also, is there any good way to make the legend not cover the plots after it's saved (see picture linked above)?
The way I save it is by creating a temporary figure with 'visible' 'off' and doing a copyobj of axes and legend, then saving. Otherwise it saves the whole figure.
The reason for which the code provided in the answer to reverse ordering of legend colors in matlab bar plot does not work in your case is because in that case (plot of a bar chart) the object in the legend are patches while in your plot they are lines.
The FaceColor only applies to patches and not to lines.
The easiest way to solve you probles should be to reverse the order in which you plot the lines "since the beginning" and, doing that, directly using the set of colors you extract from the colormap.
Nevertheless, if you wnat to work with the legend after having plotted the graph, beside revrsing the items in the legend you have also to change the color of the lines in the plot, if you want to use the set of color extracted from the colormap (at present in your picture, some lines share the same color).
To can solve the problem in two steps:
revert the string in the legend and change the colors of the line sin the plot
update the color of the lines in the legend accordingly
The two steps are required since, when you change the color of the lines in the plot, the items in the legend are updated automatically.
With reference to the code youy've posted: you can access to the legend's string and line's color through the array b.
You can access to the handles of the lines plotted as follows:
p_h=get(gca,'children')
Since you have plotted 10 lines, the array b is build as follows:
b(1:10) contains the handles to the string
b(11:2:30) contains the handles to the lines
b(12:2:30) contains the handles to the markers
To chenge the positin of the legend you can set its location property: to put it outside the axes you can set it either to:
'NorthOutside' outside plot box near top
'SouthOutside' outside bottom
'EastOutside' outside right
'WestOutside' outside left
'NorthEastOutside' outside top right (default for 3-D plots)
'NorthWestOutside' outside top left
'SouthEastOutside' outside bottom right
'SouthWestOutside' outside bottom left
In the following you can find the code in which the above suggestions have been implemented.
figure
% Initial plot
h_p=plot(0:.1:2*pi,bsxfun(#plus,sin([0:.1:2*pi]),[3:3:30]'),'linewidth',3)
% Initial legend
[h_leg,b]=legend(cellstr(strcat(repmat('sin(x)+',10,1),num2str([3:3:30]'))))
%
% YOUR CODE TO GENERATE NTHE NEW COLORS
%
map = colormap; % current colormap
n = size(b,1);
z = linspace(size(map,1),1,n/3); % there is 1 text and 2 line elements for every data series, so I divide by 3
z = round(z); %otherwise matlab gets angry that indices must be real integers or logicals
MAP = map(z(:),:); % gets elements specified by linspace from colormap
%
% Reverse the legend strings
%
rev_str=flipud(get(b(1:10),'string'))
%
% Get the handles of the lines in the legend
%
b1=b(11:2:30)
%
% Revere the string in the legend
% and update the color of the lne in the plot using the colors defined in
% MAP
%
p_h=get(gca,'children')
for i=1:10
set(b(i),'string',rev_str{i})
set(p_h(i),'color',MAP(i,:),'linewidth',3)
end
%
% Reverse the color of the lines in the legend
for i=1:10
set(b1(i),'color',MAP(i,:),'linewidth',3)
end
%
% Move the legend outside the axes
%
set(h_leg,'location','NorthEastOutside')
Original Plot
Updated plot
Hope this helps.
Related
A colleague has passed me a .fig file that has many lines on the same plots and they are coloured based on which group they belong to. The figure is shown below for reference.
I need to change the legend so that lines with the same colour have the same legend entry. The problem is that I don't have access to the raw data so I can't use the method mentioned here so is there a way to change the legend entries just using the .fig file? I tried changing some of the legend names to NaN in the property inspector but that just changes the entries to NaN.
If you have the *.fig file you can extract any included data with the 'get' method if you have understood the MATLAB Graphics Objects Hierarchy.
For example, see the left plot below as example of your *.fig file. you can extract the data in there by digging through the Children of your current figure object.
% Open your figure
fig = openfig('your_figure.fig');
% fig = gcf % If you have the figure already opened
title('loaded figure')
% Get all objects from figure (i.e. legend and axis handle)
Objs = get(fig, 'Children');
% axis handle is second entry of figure children
HA = Objs(2);
% get line objects from axis (is fetched in reverse order)
HL = flipud(get(HA, 'Children'));
% retrieve data from line objects
for i = 1:length(HL)
xData(i,:) = get(HL(i), 'XData');
yData(i,:) = get(HL(i), 'YData');
cData{i} = get(HL(i), 'Color');
end
xy data of all lines in the figure is now extracted to xData and yData. The color information is saved to cell cData. You can now replot the figure with a legend the way you want (e.g. using the SO solution you found already):
% Draw new figure with data extracted from old figure
figure()
title('figure with reworked legend')
hold on
for i = 1:length(HL)
h(i) = plot(xData(i,:), yData(i,:), 'Color', cData{i});
end
% Use method of the SO answer you found already to combine equally colored
% line objects to the same color
legend([h(1), h(3)], 'y1', 'y2+3')
Result is the plot below on the right, where each color is only listed once.
Not sure what is going wrong here. I created a minimal example below:
clear
steps = 1:6;
labels = cell(length(steps),1);
xvals = 1:10;
fig = figure(1);
ax = axes('parent',fig);
hold on
for ii=1:length(steps)
s=steps(ii);
yvals = zeros(length(xvals)) + ii;
labels{ii} = ['gain = ' num2str(s)];
plot(ax,xvals,yvals);
end
legend(ax, labels);
hold off
And the result on my system is:
With less lines it can even put colors in the legend that aren't even on the plot. What is happening?!
Explanation of what's happening
The problem is in the line
yvals = zeros(length(xvals)) + ii;
This creates a 10x10 square matrix, not a 1x10 vector. plot then plots each column of that matrix against xvals. That causes a mixing up of colors which is probably not what you want.
It's interesting to analyze specifically what happens. Matlab plots each column of that matrix with a different color, using the default cycle of colors, which in Matlab R2014b onwards is
But all columns of that matrix are the same, so each covers (overwrites) the preceding one, and you only see the last color.
Now, the color cycle has 7 colors and the matrix has 10 columns. So in the first iteration the last plotted column (the one you see) has color mod(10,7)==3 (yellow). In the second iteration you cycle through 10 more colors starting from 3, that is, you get color mod(3+10,7)==6 (light blue). And so on. Thus the color you see in the figure depends on the loop index ii, but not in the way you expected.
legend creates its entries by picking the color (and line spec) of each plotted line, in the order in which they were plotted. There are 10*6==60 plotted lines, each corresponding to a column of a matrix. But since you only supply six strings to legend, it just picks the first six of those lines, and uses their colors to create the legend entries. Those colors follow the default order, as explained above.
None of those first six lines that make it into the legend are actually seen in the figure, because they are covered by other lines. But legend doesn't care about that. So you get six legend entries with the default color order, which of course doesn't match the lines you actually see in the graph.
Solution
From the above, the solution is clear: replaced the referred line by
yvals = zeros(1, length(xvals)) + ii;
to create yvals as a vector (not a matrix). That way you'll get the figure you expected:
If I open a previously (before R2014b) saved figure, the colors I used, for instance r, k , ... would appear according to the colormap they have been saved with. What is the fast way to convert the colors to their equivalent colors in the new colormap parula.
By equivalent colors I mean the standard sequence of colors that MATLAB utilizes when we usehold on command after each plot command, without setting the color property in the `plot'. something like this:
plot(x,y1);hold on;plot(x,y2);
It should be pretty much automatic If I change the default colormap of the plot, but it is not. Is there a command for that?
The plots I have, each includes more than 20 curves that makes it annoying to change the colors manually.
The following seems to work.
open example_figure.fig %// open old file in R2014b
ax = gca;
ch = ax.Children; %// the children are lines or other objects
co = ax.ColorOrder; %// this is the new set of colors
M = size(co,1);
p = 1; %// index of new color
for n = numel(ch):-1:1 %// start with lines plotted earlier. These have higher index
if strcmp(ch(n).Type,'line') %// we are only interested in lines
ch(n).Color = co(mod(p-1,M)+1,:); %// cycle through new colors
p = p + 1; %// increase color index
end
end
The key is that, as stated in Loren's blog,
The line colors used in plots are controlled by the ColorOrder property of the Axes object.
This is the property that stores the new hold on-colors used in R2014b. But this property applies to newly plotted lines, not to those already present from the file. So what my code above does is apply the colors defined by ColorOrder to the Children of the axes that are of type 'line'.
I've observed (at least in R2010b) that newer plots have lower indices within the children array. That is, when a new plot is added to the axes, it gets the first position in the Children array, pushing older plots to higher indices. That's why in the for loop above the children index (n) is descending while the new-color index (p) is ascending. This assures that the line that was plotted first (higher index) gets the first of the new colors, etc.
As an example, let's create a figure in R2010b:
plot(1:3, 'b')
hold on
plot(4:6, 'r')
plot(7:9, 'g')
The transformed figure is
I want to add legends in a subplot, bu only for certain plots.
Here is my code :
for j = 1:length(FL)
for i = 1:length(index_list)
pos=pos+1;
subplot(size(FL,1),length(index_list), pos)
legend(num2str(ms_list(i)), 'Location', 'NorthOutside');
imagesc(imread(FL{j,:},index_list(i)))
if i==1
legend(FL(j),'Location', 'WestOutside')
end
end
The subplot contains frames extracted from multiframes .tif files. Indexes of the wanted frames are in index_list (the columns). Path to the files wanted are in FL (the rows). What I want to add on the figure is the name of the file at the left of each row and the frame index for each image plotted. ms_list contains the equivalent in milliseconds of the indexes, thats actually what I want to show.
Doing like that returns "Plot empty" at each passage in the loop.
Any idea ?
Thanks
JC
From your description and code, it seems that legend isn't what you want; rather, you want a title (above the plot) and ylabel (left of certain plots). legend is to give labels to particular objects inside the plot, such as a line series for example.
for j = 1:length(FL)
for i = 1:length(index_list)
pos=pos+1;
subplot(size(FL,1),length(index_list), pos)
title(num2str(ms_list(i))); %#<---title here
imagesc(imread(FL{j,:},index_list(i)))
if i==1
ylabel(FL(j)) %#<---ylabel here
end
end
end
The reason you were getting an error is that you were applying legend to an empty set of axes. legend labels the children of axes; no children, nothing to label, hence the error.
I'm trying to color code text in a legend. (Since I'm trying to sort several plots into different categories, I can't just rely on the line colors in the legend.) I've managed to set the text color for the entire legend, but I can't manage to assign it line by line. Is this possible?
Code so far:
list={'Label 1','Label 2','Label 3'};
leg=legend(list);
set(leg,'Textcolor',[1 0 0])
sets the text color for the entire legend as red. I'd like to be able to make some red, and some black. I tried assigning the color array as an n x 3 matrix, but MATLAB doesn't like that very much. I also poked around the legend properties using get(leg), but I couldn't find anything else that seemed useful. Any suggestions?
While the answers by yuk and gnovice are correct, I would like to point out a little-known and yet fully-documented fact that the legend function returns additional handles that correspond to the legend components. From the documentation of the legend function:
[legend_h, object_h, plot_h, text_strings] = legend(...) returns
legend_h — Handle of the legend axes
object_h — Handles of the line, patch, and text graphics objects used in the legend
plot_h — Handles of the lines and other objects used in the plot
text_strings — Cell array of the text strings used in the legend
These handles enable you to modify the properties of the respective objects.
Here is the code:
legtxt=findobj(leg,'type','text');
set(legtxt(1),'color','k')
Just find out which legends correspond to which index.
To change the legend text colors individually, you have to first get the handles to the text objects, which are children of the legend object. Then you can change their text colors separately. Here's an example of how you can do it:
plot(1:10, rand(1, 10), 'r'); % Plot a random line in red
hold on;
plot(1:10, rand(1, 10), 'b'); % Plot a random line in blue
hLegend = legend('a', 'b'); % Create the legend
hKids = get(hLegend, 'Children'); % Get the legend children
hText = hKids(strcmp(get(hKids, 'Type'), 'text')); % Select the legend children
% of type 'text'
set(hText, {'Color'}, {'b'; 'r'}); % Set the colors
Note that the color order in the last line is blue then red, in reverse order of the way the labels are passed to the legend function. The above will give you the following plot: