How to add error bars on bar graph with groups?

Hi all,
I'm trying to create a bar graph with error bars.
So far, my code is:
c = categorical({'CH','VC','GC','OC','BC','SC'});
c = reordercats(c,{'CH','VC','GC','OC','BC','SC'});
y = [707, 599; 464 444; 522 475; 566 346; 1329 1384; 459 498];
std_dev = [321 271; 233 91; 202 132; 0 173; 410 850; 179 122];
title('Title'); xlabel('x-label'); ylabel('y-label');
figure
hold on
bar(c,y)
errorbar(y,std_dev,'.')
The resulting graph is attached. The error bars appear to stack on top of each other, and are between the the two bars in each pair. I'm unsure how to make each error bar match with individual bars.
If it's helpful, the data in y is annual, where the first number (i.e. 707, 464, 522, 566) is Year 1 and the second number (i.e. 599, 444, 475, 346) is Year 2. The groups are further broken down so that the first three groups (c = CH, VC, GC) are one set and the last three (c = OC, BC, SC) are a second set. If possible, I would also like to have the Year 1 and Year 2 be different colors, as well as the two sets. So, for example, 707 = red, 599 = blue, 459 = green, 498 = orange.
Any help getting started is greatly appreciated!
Cheers

2 Comments

What version of MATLAB are you using?
The categorical data type was introduced in R2013b. A significant change in handle graphics was introduced in R2014b.
DrWooo
DrWooo on 13 May 2018
Edited: DrWooo on 13 May 2018
I am using MATLAB R2016a, and have a trial version of 2018a.

Sign in to comment.

 Accepted Answer

Hey there,
I hope this solution covers what you have been looking for.
close all
clear all
clc
y = [707, 599; 464 444; 522 475; 566 346; 1329 1384; 459 498];
std_dev = [321 271; 233 91; 202 132; 0 173; 410 850; 179 122];
num = 6; %number of different subcategories
c = 1:num;
%%Figure
figH = figure;
axes1 = axes;
title('Title'); xlabel('x-label'); ylabel('y-label');
hold on
%%Bar(s)
%You can not color differently the same bar.
for i = 1:num
bar(c(i)-0.15,y(i,1),0.2);
bar(c(i)+0.15,y(i,2),0.2);
end
%%Errorbar
errH1 = errorbar(c-0.15,y(:,1),std_dev(:,1),'.','Color','b');
errH2 = errorbar(c+0.15,y(:,2),std_dev(:,2),'.','Color','r');
errH1.LineWidth = 1.5;
errH2.LineWidth = 1.5;
errH1.Color = [1 0.5 0];
errH2.Color = [1 0.3 1];
%%Set x-ticks
set(axes1,'Xlim',[0.5 5.5]);
set(axes1,'XTick',[1 1.5 2 2.5 3 3.5 4 4.5 5 5.5 6],'XTickLabel',...
{'CH',' ','VC',' ','GC',' ','OC',' ','BC',' ','SC'});
end
If you need more details/comments about the code just let me know. Just the two main problems:
  • Your problem is not that simple... e.g.: you plotted only one bar - which means you have only 2 different data s subsets, that is logically means two different colors.Now to color differently the "subsubsets" ('CH','VC' etc.) and even every bar(!) you have to create a bar for every different colors...
  • The other thing is that you can not position freely your objects when you are working with categorical type. To bypass that just create simple vectors as I did.

7 Comments

This works great! Thanks for your help.
One final question on the color of the bars -- is it possible to have it look like the original figure posted, where its red/blue pairs? Or, how would you start to individually color each bar?
Thanks for your help.
Ok modify the previously posted code's Bar section, the following code colors all of the pairs red-blue.
%%Bar(s)
%You can not color differently the same bar.
barColor1 = [0 0.4470 0.7410]; %blue
barColor2 = [0.8500 0.3250 0.0980];%red
for i = 1:num
bar(c(i)-0.15,y(i,1),0.2,'FaceColor',barColor1);
bar(c(i)+0.15,y(i,2),0.2,'FaceColor',barColor2);
end
As you can see you have to modify the ' FaceColor' attribution. Just give any RGB value to it and it will color it.
If you want to have differently shaded red-blue pairs I advise you to define a colormap and use its elements. (Basically a matrix containing RGB color values in each row.) (If you define a colormap that contains only the red and blue all your bars will be colored red and blue like now.)
Thanks again! One thing I noticed after making this change is that the graph cuts off the last group -- so it only goes to BC, and SC is not there. I made a slight edit to increase the xLim (in the %set x-ticks section) to 6.3 instead of 5.5. This included the last group, without having CH reappear on the x-axis.
Thank you for the provided code. How would you add a "legend" to this box chart, given all different elements you had to put together to make the categorical box chart look reasonable?
I am not sure if I got what you mean, but to simply add a legend to the figure you do it like most of the time, using legend:
legend({'blue bars';'red bars'},'Location','northwest')
aNiceImage.jpg
This code has been really helpful. Thanks!
@Timon Viola, I was wondering: Could you please clarify what do -0.15 and +0.15 mean in the lines where you build the bars? :
bar(c(i)-0.15,y(i,1),0.2);
bar(c(i)+0.15,y(i,2),0.2);
Thank you.
It's the kludge to find the approximate center of the bars x-position.
See my Answer below for the way to do this without relying on such empirical constants...
Although TMW has added another visible property to the bar object, 'XEndPoints' altho not sure just which release it was in first. It is the X-axis position of the bars in each group for each handle that is the value needed at which to plot the errorbar() (or label the bar or whatever at bar center).

Sign in to comment.

More Answers (1)

dpb
dpb on 11 May 2018
Edited: dpb on 13 May 2018
See <Errors on Bar> to retrieve the locations of the various bars' offsets from the nominal group center.
I would urge to add your complaint to the making of such obviously-desired features so difficult in BAR() by submitting enhancement request/support request. As noted, why TMW would hide the necessary properties is beyond ken. Shows extreme lack of forethought at best; in reality the whole interface ought to be redesigned; it's a klutz.
ADDENDUM
Had a few minutes; hmm....I see the XTick values from the barplot object are returned as the categorical values; not useful for errorbar. OK, go at it this way...
hBar=bar(double(c),y); % plot against the underlying value
hAx=gca; % handle to the axes object
hAx.XTickLabel=categories(c); % label by categories
hold on
X=cell2mat(get(hBar,'XData')).'+[hBar.XOffset]; % compute bar locations
hEB=errorbar(X,y,std_dev,'.') % add the errorbar
for i=1:length(hEB) % See Note...
hEB(i).Color=hBar(i).Facecolor;
end
Above yields
NB: There's a syntax to set multiple handles w/o the loop but its complicated enough I can never get it right the first time or two; see the doc for set for details if want to eliminate the loop as a project... :)
set(hEB,{'Color'},get(hBar,'FaceColor'))
I always forget you have to turn the property name into cellstr to do multiple objects...
NB2: The key feature here is the use of the hidden 'XOffset' property to retrieve the actual midpoint of the bars in each group--this code will work correctly irregardless of the number of groups/number per group, the other Answer uses an approximate constant offset that is specific for only six and two and will have to be modified manually. It's also not quite the right value; close, but not what ML uses internally --
>> [hBar.XOffset] % internal offset
ans =
-0.1429 0.1429
>>
NB3: This also automates setting the colors to match precisely.
NB4: Reiterate to beat on TMW for how difficult is to use BAR() and this about the way categorical is treated just makes it worse...

4 Comments

Thanks for the advice! I was unable to fully follow this code, however I will be sure to leave a comment for Matlab regarding the surprising difficulty of this.
dpb
dpb on 13 May 2018
Edited: dpb on 13 May 2018
"I was unable to fully follow this code..."
In what regards and where and why? It's reasonably straightforward after one knows about the hidden property...if you're interested in that level of detail, I discovered it via Yair's "Undocumented Matlab" site; there's another Q? here in Answers that showed how to uncover; I'll see if can find and post the link instead of duplicating yet again...
Well, I don't find that posting I was sure had done; here's link to Yair's blog page with background. <Y Altman's blog undocumented object properties>
And, the FEX downlowad link -- <getundoc>
This was a great solution; I've been looking all over for a solution to this problem, and yours is by far the best. Thank you much!
I would suggest to add your voice via contact link on enhancement need and the overall bar() interface, too. Maybe eventually TMW will finally do something about it.

Sign in to comment.

Categories

Asked:

on 11 May 2018

Commented:

dpb
on 2 Dec 2020

Community Treasure Hunt

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

Start Hunting!