MATLAB Answers

Order of legends in stacked bar plot

262 views (last 30 days)
Ingrid
Ingrid on 27 May 2015
Edited: Adam Danz on 15 Apr 2021
Hi,
I want to make a stacked bar plot with legend, like the one below, but I find it annoying that the order of the legend is reversed. The yellow is on top of the bars but at the bottom in the legend. Is there a way to fix this?
Thanks in advance, Ingrid
  2 Comments
Adam Danz
Adam Danz on 15 Apr 2021
@Scott MacKenzie, I agree, many of these answers are either difficult to understand due to missing variable values, formatting, the use of undocumented legend outputs that cause problems, or contain unnecessary 3rd party functions. I've added an answer to show how to flip the legend components.

Sign in to comment.

Answers (6)

Tom Schoehuijs
Tom Schoehuijs on 24 Apr 2018
Hi! For 2017b the suggestions don't work... I made a small program that will flip the legend entries: fliplegend.
Just plot the bar plot and run this program as you would run the legend:
figure();
bb=bar(data, 'Stacked','FaceColor','flat');
labels={'label1','label2'};
fliplegend(labels);

Dasharath Gulvady
Dasharath Gulvady on 28 May 2015
You can use the first input argument to the "legend" command to specify the handle order. Here are two examples:
figure subplot(2,1,1) Y = round(rand(5,3)*10); hb=barh(Y,'group'); legend(fliplr(hb),'row 1', 'row 2', 'row 3');
subplot(2,1,2) hb=bar(Y,'group'); hold on hp=plot(1:5); legend([hb hp],'row 1', 'row 2', 'row 3', 'plot');
  3 Comments
Dominik Wiedenhofer
Dominik Wiedenhofer on 21 Jan 2016
You are completely right in pointing out that my single code line of code does not exactly provide what I am looking for. This is my mistake, because I used to do two lines:
legend_h = legend(labels{end:-1:1});
set(legend_h,'YDir','reverse');
the {end:-1:1} reverses the labels, the YDir reverse then also switches the colours. But the second line of code is not working anymore in 2015a, I only get an error message:
Error using matlab.graphics.illustration.Legend/set
There is no YDir property on the Legend class.
any ideas?

Sign in to comment.


Dominik Wiedenhofer
Dominik Wiedenhofer on 26 Jan 2016
based on some forum crawling I found a solution for anyone who might be interested:
[~, h2] = legend(fliplr(labels),'Location','NorthWest');
cmap_cust = colormap;
n = size(h2,1);
MAP = cmap_cust(round(linspace(size(cmap_cust,1),1,n/2)),:); %// n is as my code above
for k = (n/2 + 1):n;
a1 = get(h2(k),'Children');
set(a1,'FaceColor',MAP(k-n/2,:));
end
  1 Comment
Mike Garrity
Mike Garrity on 26 Jan 2016
I think you would be much better off following Dasharath's advise here. His approach is simple and reliable.
With your approach, you are getting the picture you want:
h = bar(magic(4),'stacked')
labels = {'First','Second','Third','Fourth'};
[~, h2] = legend(fliplr(labels),'Location','NorthWest');
cmap_cust = colormap;
n = size(h2,1);
MAP = cmap_cust(round(linspace(size(cmap_cust,1),1,n/2)),:);
for k = (n/2 + 1):n;
a1 = get(h2(k),'Children');
set(a1,'FaceColor',MAP(k-n/2,:));
end
But you've scrambled the connectivity between the legend and the bar chart, as you can see here:
set(h(2),'DisplayName','Fred','FaceColor','red')
This is an improvement over the YDir approach in previous releases because the label and icon are staying together, but it is still rather misleading and might cause you trouble down the road.
What's going on here is that the form of legend which returns a handle as the second argument is a compatibility mode which disables a lot of the auto-updating features of the legend. This allows you to do things like making the icon colors different from the object colors. But some of the auto-updating is still happening. This can have surprising results.

Sign in to comment.


Dominik Wiedenhofer
Dominik Wiedenhofer on 4 Feb 2016
Dasharath & Mike are completely right; my previous attempt at solving this makes a lot of problems when further editing a figure - at some points the colors & ordering suddenly reverses. Therefore Dasharath's solution is definitly best and I actually got it to work now.

Cyril Siman
Cyril Siman on 7 Sep 2017
Hi, everyone. I have a similar problem like you guys. I need plot scatered graph, but my legend itemms are not in same order like in graph.
I use fliplr and flipud function, but is not working. Problably I don't understand them.
Here is my code:
TBdata = [2006 70147.6 15757.9 13698.8 2007 84165.8 19187.6 15764.6 2008 92217.6 19828.2 16232.1 2009 73166.4 13866.1 9798.1 2010 85068.2 12797.9 8002.1 2011 90982.2 14982.2 10108.4 2012 98787.2 18898 13038.3 2013 110714.4 20306.7 14534.1 2014 116029.5 22497.4 16366.4 2015 111892.1 21286.5 15868.7];
years = TBdata(:, 1); dusikate = TBdata(:, 2); fosforecne = TBdata(:, 3); draselne = TBdata(:, 4);
h = bar(years, [dusikate fosforecne draselne], 0.5, 'stack'); title({'Celková spotreba draselných, fosforečných a dusíkatých hnojív (v tonách) na';'sledovanej výmere ornej pôdy na Slovensku v období rokov 2005/2006 - 2014/2015'},'FontWeight','normal','FontName','Arial','FontSize',20) xlabel('sezóna (hospodársky rok)','FontWeight','bold','FontName','Arial','FontSize',16) ylabel('spotreba hnojív v tonách [t]','FontWeight','bold','FontName','Arial','FontSize',16,'Units','Normalized','Position',[-0.06, 0.5, 0]) axis([2005 2016 50000 180000]) set(h(1),'FaceColor','blue') set(h(2),'FaceColor','green') set(h(3),'FaceColor','red') h = get(gca,'xlabel'); % get handle of xlabel pos = get(h,'position'); % get current position of label ylimits = get(gca,'ylim'); % change position to be below x-axis by 8% pos(2) = ylimits(1) - 0.06 * (ylimits(2) - ylimits(1)); set(h,'position',pos) set(gca, 'XTick', 2006:2015) ax = gca; ax.YAxis.Exponent = 0;
labels = {'dusikate','fosforecne','draselne'}; legend(flipud(labels),'Location','eastoutside')
grid on set(gcf, 'PaperPositionMode','manual'); set(gcf, 'PaperUnits','inches'); set(gcf, 'PaperPosition',[0.25 0.25 15.00 10.00]); set(gcf, 'PaperUnits', 'inches'); set(gcf, 'PaperType', 'A4'); set(gcf, 'PaperOrientation', 'portrait'); print('npk_spolu','-dpng','-r300');
Any suggestions for my ?
Thank's

Adam Danz
Adam Danz on 15 Apr 2021
Edited: Adam Danz on 15 Apr 2021
Stacked bars are stacked upward. That means the bottom layer of bars show column 1 of the input matrix and the top later of bars show the last column. The legend labels, on the other hand, are stacked in the order that the graphics objects were created, by default.
If you want to reverse the order of stacked bar elements in the legend, simply flip the legend handle along with the corresponding legend strings.
% data
rng(210415)
x = randi(100,4,5);
labels = {'A','B','C','D','E'}; % for each column of x
% Default legend order
figure()
subplot(1,2,1)
h = bar(x,'stacked');
legend(h, labels, 'Location', 'NorthWest')
title('Default legend order')
% Flipped legend order
subplot(1,2,2)
h = bar(x,'stacked');
legend(flip(h), flip(labels), 'Location', 'NorthWest') % <--- the only difference
title('Flipped legend order')
Problems with other solutions proposed here
A variety of solutions is a good thing and is encouraged. Some users may be wondering why other solutions here may not work for them. Here's a review of the solutions.
1. Tom Schoehuijs's fliplegend function has the right idea but it acts on the current figure rather than providing the axis handle as an input. Functions like gcf/gca/gco etc should be avoided whenever possible. Plus, the solution is quite simple and shouldn't require having access to an external function.
2. Dasharath Gulvady's solution is also perfectly fine (+1) but with one minor criticism other than the lack of readability due to its formatting. The legend strings are hard coded. This isn't just being nit-picky. It's a problem because it may not be obvious to users that by flipping the bar handles, you need to list the strings in reverse order of the columns of data. The first column of data is labeled "Row 3", while the third column of data is labeled "Row 1". It's better to maintain the association betwen label(n) with column(n) of the data.
3. Several solutions offered here use undocumented outputs to legend(). Requesting more than the first output to legend() is problematic as is noted in the r2016a documentation of legend(), this syntax is not recommended and creates a legend that does not support all graphics features. Example of problem caused by undocumented outputs to legend: fontsize problem. There is no need to access these undocumented, problematic outputs and they will likely become unavailable in future releases.
4. There are a couple comments that suggest setting the legend's YDir to reverse. Firstly, ydir is no longer a property of legends. Secondly, as Mike Garrity mentioned in a comment, this never was a working solution to begin with since it only flipped the the icons and not the labels.
  2 Comments
Adam Danz
Adam Danz on 15 Apr 2021
The way I think about it is that legend(h, str) represents all objects in the graphics array h and labels them with the strings in str in the order they appear in h and str.
By default, the stacked bar handles are ordered from the bottom-up so h(1) are handles to the bottom row of stacked bars. That's why the first icon in the legend is the bottom row of bars by default.
We're not really tricking the legend function, we're just flipping the order of inputs.
Here's a related example. The left subplot shows the default order of objects in the legend (the order in which they were created). The subplot on the right reorders the handles and strings.
figure
subplot(1,2,1)
hold on
box on
h(1) = plot([0,1], [1 1], 'b-','LineWidth',4);
h(2) = plot([0,1], [2 2], 'r-','LineWidth',4);
h(3) = plot([0,1], [3 3], 'g-','LineWidth',4);
legStr = {'blue','red','green'};
legend(h, legStr)
ylim([0,4])
title('Default order')
subplot(1,2,2)
hold on
box on
h(1) = plot([0,1], [1 1], 'b-','LineWidth',4);
h(2) = plot([0,1], [2 2], 'r-','LineWidth',4);
h(3) = plot([0,1], [3 3], 'g-','LineWidth',4);
idx = [2,1,3];
legend(h(idx), legStr(idx))
ylim([0,4])
title('Reorderd to [2,1,3]')

Sign in to comment.

Community Treasure Hunt

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

Start Hunting!