Order of legends in stacked bar plot

112 views (last 30 days)
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
Scott MacKenzie
Scott MacKenzie on 15 Apr 2021
I've just read all the comments on this question, including two from Staff. Fixing the issue Ingrid notes should not require a custom function or other programming approaches. My gosh. That certain data patterns need a revered order for the legend entries is a no-brainer. And doing so should utterly simple, for example, via a YDir legend property (or something equally as simple), as Dominik notes. This can't be too hard for the MathWorks folks to implement and include in a future release. But, as the question first arose in 2015, that doesn't seem likely. Too bad.
Adam Danz
Adam Danz on 15 Apr 2021
Edited: 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.

Accepted Answer

Afiq Azaibi
Afiq Azaibi on 18 Sep 2023
Starting in 23b, legend has a 'Direction' property which allows you to reverse the entries of a legend. There is an additional feature which will reverse the legend by default for certain charts such as area and stacked bar charts:
bar(magic(5), 'stacked');
l = legend;
l.Direction
ans = 'reverse'
If you want to opt out of this behavior, you can either pass an object handle to the legend or set the Direction property to 'normal':
b = bar(magic(5), 'stacked');
legend(b);
e
bar(magic(5), 'stacked');
legend('Direction', 'normal');

More Answers (6)

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.
  4 Comments
Vivienne Reiner
Vivienne Reiner on 11 Sep 2023
Adam, I love your simplicity! This worked for me very easily. Thanks so much.

Sign in to comment.


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
Mike Garrity
Mike Garrity on 21 Jan 2016
I don't think that was doing what you thought it was doing.
In releases earlier than 14b, the icons were drawn into the axes of the legend using data units and the text was drawn into the axes of the legend using normalized units. The legend code was assuming that YDir='normal'. When you change the YDir, the icons are getting moved, but their corresponding labels are not getting moved. That means that the names of the objects aren't next to their icons.
Consider this example:
h = bar(magic(4),'stacked');
l = legend('Location','eastoutside');
set(l,'YDir','reverse')
set(h(3),'DisplayName','This one','FaceColor',[.75 .75 .75])
The object named 'This one' is grey, but the legend shows it as cyan.
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

Community Treasure Hunt

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

Start Hunting!