How do I make a legend for data organized into colours in a bar plot?

Hello, I have data (see attached figure) which is grouped together by colour. I would like to have a legend that which explains what each colour grouping is defined by. I can only find legend creation questions about creating legends on the actual x-axis data, instead of the colour by which I organized it.
Is there a way to do this? I organized my data by colour as follows:
for i = 1:length(Names)
switch Names(i)
case{'Pu','Am','Np','Cm'}
Yield.CData(i,:) = [255 0 0];
case{'Mo','Ru','Tc','Rh','Zn','Ga'}
Yield.CData(i,:) = [0 255 0];
case{'Ce','Nd','Eu','Sm','Sc','Y','La'}
Yield.CData(i,:) = [255 255 0];
case{'Li','Na','K','Rb','Cs','Fr'}
Yield.CData(i,:) = [0 0 0];
case{'O','Xe','Te'}
Yield.CData(i,:) = [255 255 255];
end
end
If someone could provide an answer or some insight I would greatly appreicate it.
~ Dan

 Accepted Answer

OK, with your data file...
[Yield,Elements] = xlsread('test.xlsx');
[YS,iY]=sort(Yield,'descend'); % index to plot by size
FigureName = '';XaxisLabel ='Element';YaxisLabel='Yield (atomic %)';
hF=figure('Name',FigureName,'color','white');
hBY = bar(YS); % use different, identifiable variable for handle
hBY.FaceColor = 'flat';
for i = 1:length(Elements)
switch Elements{iY(i)} % get in sorted order
case{'Pu','Am','Np','Cm'}
hBY.CData(i,:) = [255 0 0]; % red for actinides
case{'Mo','Ru','Tc','Rh','Zn','Ga'}
hBY.CData(i,:) = [0 255 0]; % green for noble metals
case{'Ce','Nd','Eu','Sm','Sc','Y',}
hBY.CData(i,:) = [0 0 255]; % blue for lanthanides (R.E.E.)
case{'Li','Na','K','Rb','Cs','Fr'}
hBY.CData(i,:) = [0 0 0]; % black for alkali-metal halides
case{'O','Xe','He','Ne','Ar','Kr',}
hBY.CData(i,:) = [255 255 255]; % white for non-metals
end
end
hAx=gca; % axes handle for munging with
hAx.XTick=1:numel(Y);
hAx.XTickLabel=Elements(iY);
hAY.YAxis.TickLabelFormat='%0.3f'; % set consistent format string
xlabel('Element','FontName','Optima','FontSize',10);
ylabel('Yield (atomic %)','FontName','Optima','FontSize',10);
title('Yield by Element Classification','FontName','Optima','FontSize',16);
% Now add dummy bar plot to make legends match colors
hold on
% colors -- desired color for each type/class
%co=get(groot,'defaultAxesColorOrder'); % get current axes color order
colors=[[1 0 0]; ... % red for actinides
[0 1 0]; ... % green for noble metals
[0 0 1]; ... % blue for lanthanides (R.E.E.)
[0 0 0]; ... % black for alkali-metal halides
[1 1 1]]; % white for non-metals
nColors=size(colors,1); % make variable so can change easily
labels={'Actinide';'Noble metal';'Lanthanide';'Alkali halide';'Non-metal'};
hBLG = bar(nan(2,nColors)); % the bar object array for legend
for i=1:nColors
hBLG(i).FaceColor=colors(i,:);
end
hLG=legend(hBLG,labels,'location','northeast');
producesuntitled.jpg

9 Comments

One "issue" with the above is there is no label to associate with the default blue color and it is awfully close in appearance to the solid blue.
One solution would be to continue on with what I started when I retrieved the default color order in order to add the first element from it to the colors array -- then you could create one more row in the dummy array and label the last row as 'Others' with the default color.
co=get(groot,'defaultAxesColorOrder'); % get current axes color order
colors=[[1 0 0]; ... % red for actinides
[0 1 0]; ... % green for noble metals
[0 0 1]; ... % blue for lanthanides (R.E.E.)
[0 0 0]; ... % black for alkali-metal halides
[1 1 1]; ... % white for non-metals
co(1,:)]; % first in default color order
nColors=size(colors,1); % make variable so can change easily
labels={'Actinide';'Noble metal';'Lanthanide';'Alkali halide';'Non-metal';'Other'};
hBLG = bar(nan(2,nColors)); % the bar object array for legend
...
Sorry about the delay in getting back to you, I had a busy weekend sorting out some experimental stuff. Anyways, I looked through this idea and implemented it into my code and it seems to work very well. I have learned a bunch through this iterative process as well so I wanted to say thank you for that and for taking the time for sticking with it for a couple of days. My figure looks good now, and I can change the data between different files and now they will order accordingly. This is actually very valuable for my code as it turns out.
I am planning on using my code to eventually produce figured for my thesis (MASc). If you would like to be referenced for your contribution to my code I have absolutley no problem with that. If you want you can supply me with an email adress or something and I can get your contact info for a reference if you would like:)
Let me know! and again, thanks so much.
I can be contacted via contributors link if care to...
i exactly desire this but with simple data plots not bar... but facecolor property not working with plot... Can anyone guide ...
The line color property is 'Color' - see plot input description, examples...
picked the point but sory now i m stuck in "CDATA" property :-( sharing a sample data set.. kindly guide further with code
first_data = [[3 4 8];[3 6 4];[1 7 2]]; % desired legend (first) & color (green)
second_data = [[1 3 5];[3 2 7];[3 4 2]]; % desired legend (second) & color (cyan)
combine = {'first_data','Second_data'}; % recieving new data in every 'for' loop iteration thats why combining
colours = {'g','c'};
Legend_Names = {'first','second'};
for i = 1:length(combine)
plot(cell2mat(combine(i)),'Color',cell2mat(colour(i))); grid on; hold on;
end
CData is a property of patches, not lines...
combine is not a numeric variable suitable to be plotted -- no klew as to what you're trying to achieve here...this is totally off track of the original Q?; I suggest creating a new Q that explains fully what you're trying to do.

Ok Agree.. i will Create a new Question... Thanks for Guidance

Sign in to comment.

More Answers (1)

The bar plot/object in Matlab is a really, Really, REALLY difficult thing to work.with, unfortunately.
A single bar plot has only one bar object as you've drawn it above and, therefore, you can only assign one legend string because there is only a single graphic object on the plot to assign the legend to.
You can "fake" it by creating a hidden grouped bar with two rows by the number of columns that you have colors, in this case five (5) and then assigning those bar objects the desired colors and use legend() on them.
A dummy example with the "real" data being five bars and four dummy colors
hB=bar(abs(randn(1,5))); % generates one bar handle with five bars
hB.FaceColor='flat'; % ready to set color by bar w/ CData
hB.CData(1,:)=[255 0 0]; % first and
hB.CData(3,:)=[255 0 0]; % third red
hB.CData(2,:)=[0 255 0]; % second green
hB.CData(5,:)=[255 255 255]; % fifth white, leaves fourth default
hold on % set hold state to add onto plot
hBB=bar(nan(2,4)); % now create the dummy four bar handles
hBB(1).FaceColor=[1 0 0]; % FaceColor is normalized 0, 1 instead of rgb -- red
hBB(2).FaceColor=[0 1 0]; % green
hBB(3).FaceColor=[0 0 1]; % blue
hBB(4).FaceColor=[1 1 1]; % white
hLG=legend(hBB,'A','B','C','D','location','northwest'); % write legend for the colored bars
The above creates the following figure--
I've railed for years about bar; TMW really should totally redesign a new replacement that isn't so difficult to use for obvious needs.

6 Comments

Thanks dpb, I am going to have a thorough read through of this tonight and see if I can get it to work. It is unfortunate that legend making is such a pain. I don't see why this is something they wouldnt be able to easily address. Thanks again for getting back to me so fast!! :)
It's really not the legend() part that's hard; it's the way they designed bar() such that a single column or row generates only one graphic object.
It's the kludge to create the number of bar() object handles that you need to match the number of legends you want to write that's the pain.
It's possible you might be able to recast your data into a grouped format to generate the number of handles needed directly but I just approached to add the legend to the existing plot.
If you'd attach a sample dataset, if had time I might try to see if can go at it in a slightly more direct manner.
I think I understand what you are saying, now that I tried to impliment your suggestion into my code. I am fairly new to MATLAB (and any type of coding in general) so it takes me a while to catch up!
The point that I think I understand it, is that by implimenting this idea into my code, it doesn't overlay the legend onto the same plot, but instead makes its own figure window and shows the legend there with the dummy/fake bar graph. Here is the code that I have (with sensitive info taken out):
FigureName = 'BLANK';XaxisLabel ='ELEMENT';YaxisLabel='Yield (atomic %)';
figure('Name',FigureName,'color','white');
[Yield,Elements] = xlsread('TEST.xlsx',1,'A1:B24');
Yield = bar(Yield);
Yield.FaceColor = 'flat';
for i = 1:length(Names)
switch Names(i)
case{'Pu','Am','Np','Cm'}
Yield.CData(i,:) = [255 0 0]; %red for actinides
case{'Mo','Ru','Tc','Rh','Zn','Ga'};
Yield.CData(i,:) = [0 255 0]; %green for noble metals
case{'Ce','Nd','Eu','Sm','Sc','Y',};
Yield.CData(i,:) = [0 0 255]; %blue for lanthanides (R.E.E.)
case{'Li','Na','K','Rb','Cs','Fr'}
Yield.CData(i,:) = [0 0 0]; %black for alkali-metal halides
case{'O','Xe','He','Ne','Ar','Kr',};
Yield.CData(i,:) = [255 255 255]; % white for non-metals
end
end
xticklabels(Names);xticks(1:1:length(Names));
xlabel(XaxisLabel,'FontName','Optima','FontSize',16);
ylabel(YaxisLabel,'FontName','Optima','FontSize',16);
title('BLANK','FontName','Optima','FontSize',16);
a = get(gca,'XTickLabel');
set(gca,'XTickLabel',a,'FontName','Optima','fontsize',12);
xaxisannotlocation = 14;yaxisannotlocation = 1.42*10^(-3);
Annotation = {'BLANK ','BLANK ','BLANK '};
TEXT = text(xaxisannotlocation,yaxisannotlocation,Annotation,'FontSize',10,'FontName','Optima');
set(TEXT, 'horizontalAlignment', 'right','verticalAlignment', 'top')
set(TEXT, 'units', 'normalized')
set(TEXT, 'position', [1 0.97 0])
dummybar = bar(nan(2,5));
dummybar(1).FaceColor=[1 0 0];
dummybar(2).FaceColor=[0 1 0];
dummybar(3).FaceColor=[0 1 0];
dummybar(4).FaceColor=[0 0 0];
dummybar(5).FaceColor=[1 1 1];
legend(dummybar,'A','B','C','D','location','northwest');
%[im_hatch,colorlist] = applyhatch_pluscolor(gcf,'\/+c.x',0);
%imwrite(im_hatch,'im_hatch.png','png');
The data that I read in is from an excel sheet where one column is element names and the adjacent column is yield. The attached .xls can be read in.
Hopefully this is all correct and works. If you do get a chance to take a look I would greatly appreciate it! The code messes up where I tried to impliment your idea, if you comment out the dummy stuff at the end you can see what figure I am trying to overlay the legend onto :)
"... it doesn't overlay the legend onto the same plot, but instead makes its own figure window and shows the legend there with the dummy/fake bar graph."
No. Altho I see I did omit the key line hold on before creating the dummy bar plot objects to overlay the existing "real" bar plot that is the desired data.
I fixed that oversight in the answer; insert the same in your code before the dummybar= line
oh wow I should have thought of that. Thank you so much, this works well. I really appreciate your help dpb!:)
I tried to recast the problem to use a 2D array to generate an array of bar handles to color but BAR() is just too inflexible--when you create a grouped bar, then it forces the center of each group to the location of the row index in the input array and won't let you locate individual bars.
The only alternative to the above "trick" I see is to create N bar plots, each of which then contains only the desired elements for the given set, but has NaN for all the other elements. Building those arrays is also a convoluted mess of coding.
All in all, my previous remarks still hold--TMW needs to introduce a whole new barplot routine that one could do something with.
I did rewrite your code a little and posted a second answer...but yours will work with just the addition of the hold on line I didn't get pasted in the example and fixing up some of the labels and so on. I'm not sure what your intent was there...

Sign in to comment.

Products

Release

R2018a

Asked:

on 8 Mar 2019

Commented:

on 25 Feb 2025

Community Treasure Hunt

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

Start Hunting!