error bars in bar plot with categories

211 views (last 30 days)
Hello
I have a bar plot with 3 categories where I want to insert error bars.
I've been trying it for a long time, but I can't find the solution.
x are the categories
y the values
errorplus and errorminus the errors values.
Can someone hel me?
Thank you very much!
x=categorical({'Hip';'Knee';'Ankle'});
y=[21.0416,19.5461,50;30.003601074218750,24.056737899780273,50;22.604316711425780,26.007211685180664,35];
errorplus=[0.434635639190674,1.783290743827820,0;1.722714424133301,1.000267505645752,0;0.471254885196686,2.722285032272339,0];
errorminus=errorplus;
figure;
bar(x,y);
title('Range of Motion','FontSize',18)
ylabel('Angle Values [°]','FontSize',18)
set(gca,'linewidth',2,'FontSize',14)
ylim([0 60]);

Accepted Answer

Star Strider
Star Strider on 12 Dec 2019
This seems to be an XOR situation. Apparently, it is possible to have categorical x-values or errorbar bars but not both.
This requires non-categorical x-values, although it produces the appropriate final result:
x=categorical({'Hip';'Knee';'Ankle'});
y=[21.0416,19.5461,50;30.003601074218750,24.056737899780273,50;22.604316711425780,26.007211685180664,35];
errorplus=[0.434635639190674,1.783290743827820,0;1.722714424133301,1.000267505645752,0;0.471254885196686,2.722285032272339,0];
errorminus=errorplus;
figure;
bar(x,y);
hBar = bar(y, 0.8); % Return ‘bar’ Handle
for k1 = 1:size(y,2)
ctr(k1,:) = bsxfun(@plus, hBar(k1).XData, hBar(k1).XOffset'); % Note: ‘XOffset’ Is An Undocumented Feature, This Selects The ‘bar’ Centres
ydt(k1,:) = hBar(k1).YData; % Individual Bar Heights
end
hold on
errorbar(ctr, ydt, errorplus, '.r') % Plot Error Bars
hold off
title('Range of Motion','FontSize',18)
ylabel('Angle Values [°]','FontSize',18)
set(gca,'linewidth',2,'FontSize',14)
ylim([0 60]);
set(gca,'XTickLabel',x)
producing this plot:
1error bars in bar plot with categories - 2019 12 12.png

More Answers (3)

Paul Himsl
Paul Himsl on 12 Dec 2019
WOW .. thank you yery much!
Is it possible to "write" the values of each bar on the top of the bar when there are error bars already?
  3 Comments
dpb
dpb on 12 Dec 2019
"Is it possible to "write" the values of each bar on the top of the bar when there are error bars already?"
Showing how to write text is the link I posted; to add them as well as the error bars just adjust the y position to add the error value to the y value (for above) or subtract for below.
Star Strider
Star Strider on 12 Dec 2019
Finally ... reinstalled and updated!
To add the values of each bar at the top, the loop changes to:
for k1 = 1:size(y,2)
ctr(k1,:) = bsxfun(@plus, hBar(k1).XData, hBar(k1).XOffset'); % Note: ‘XOffset’ Is An Undocumented Feature, This Selects The ‘bar’ Centres
ydt(k1,:) = hBar(k1).YData; % Individual Bar Heights
for k2 = 1:size(ydt,2)
text(ctr(k1,k2), ydt(k1,k2)+errorplus(k1,k2), sprintf('%.1f', ydt(k1,k2)), 'HorizontalAlignment','center','VerticalAlignment','bottom');
end
end
The inner loop is necessary in order to plot the values correctly.
1error bars in bar plot with categories (2) - 2019 12 12.png
Make appropriate changes to get the result you want.

Sign in to comment.


dpb
dpb on 12 Dec 2019
Edited: dpb on 12 Dec 2019
This is a royal pain for even numeric axes; even worse for categorical. Complain to TMW about the sorry quality of implementation for bar to not provide the necessary tools <See LINK for example w/ numeric axis> and then errorbar hasn't been enhanced to allow categorical variables on x-axis...besides, there's no concept of a fractional value for a categorical variable to let you move to the right bar position for the grouped bars. All in all, it's a royal mess of not being a complete packaged solution. Disgraceful in my opinion.
Anyway, only practical way I see is to actually plot the figure as numeric underlying data and then label the result as desired...
... % your preliminaries here...
figure;
hB=bar(double(x),y); % create the bar as numeric instead of categorical x axis
title('Range of Motion','FontSize',18)
ylabel('Angle Values [°]','FontSize',18)
set(gca,'linewidth',2,'FontSize',14)
ylim([0 60]);
hold on
% get ready to put the errorbar() on top...
err=errorplus; % all errors were same +/- so use symmetric
hEB=[]; % placeholder of the errorbar object handles
hT=[]; % ditto text handles
for i=1:numel(hB) % put the errorbar on each bar
hEB=[hEB; errorbar(hB(i).XData+hB(i).XOffset,hB(i).YData, err(:,i),'.k')];
hT=[hT text(hB(i).XData+hB(i).XOffset, ...
hB(i).YData+err(:,i).', ...
num2str(hB(i).YData.','%.2f'), ...
'VerticalAlignment','bottom','horizontalalign','center','fontsize',9)];
end
xticklabels(categories(x)) % label the ticks by categorical names
There really should be built in facilities inside bar for adding annotations w/o relying on hidden properties and so much coding.
Hadn't previously thought of the limitation in errorbar, but it should be enhanced, too, and then the dificulties in mixing the internals of a categorical axis with positions other than exactly at the category values should be addressed as well. So much work to do that TMW never seems to want to address but that would be major boosts in overall productivity to quit having to spend hours trying to find workarounds. :(
  5 Comments
dpb
dpb on 12 Dec 2019
Edited: dpb on 12 Dec 2019
Above looks ok w/o double loop to me???
HMMM...yours is Hip/Knee/Ankle whereas default order is Ankle/Hip/Knee, the order of categories of x in alphabetic order. Mayhaps there's the difference.
The data are the same for both, however, just the order of them on the x axis.
>> figure
>> bar(x,y)
results in
which is order in which OP defined the arrays. Not sure what changed the axis order in yours.
Could it be version, perhaps? Mayhaps the newer release honors the array order instead of the alphabetic of R2017b as categories here shows? What does the following return on your system (R2019 I think?)
>> x
x =
3×1 categorical array
Hip
Knee
Ankle
>> categories(x)
ans =
3×1 cell array
{'Ankle'}
{'Hip' }
{'Knee' }
>> double(x)
ans =
2
3
1
>>
dpb
dpb on 12 Dec 2019
Ah-so! I hadn't seen you threw away the bar(x,y) call by the subsequent hBar=bar(y,0.8) so the x values weren't provided to the bar object explicitly. That 'splains it all and why it took the second level addressing.

Sign in to comment.


Paul Himsl
Paul Himsl on 13 Dec 2019
Thank you all very much for your help!
  1 Comment
dpb
dpb on 13 Dec 2019
Edited: dpb on 13 Dec 2019
Glad to help where TMW falls short...
BTW, the route S-S took can also be done w/o the double loop; just have to recognize the order is different as noted in previous comment--the following generates identical plot.
hB=bar(y); % the y data versus column number instead of ordered categorical x
ylim([0 60]);
hold on
hEB=[]; % placeholder of the errorbar object handles
hT=[]; % ditto text handles
for i=1:numel(hB)
hEB=[hEB; errorbar(hB(i).XData+hB(i).XOffset,hB(i).YData, err(:,i),'.k')];
hT=[hT text(hB(i).XData+hB(i).XOffset, ...
hB(i).YData+err(:,i).', ...
num2str(hB(i).YData.','%.2f'), ...
'VerticalAlignment','bottom','horizontalalign','center','fontsize',9)];
end
xticklabels(cellstr(x)) % label in numeric, not alphabetic order
BTW, hEB, hT are the handles to the errorbar and text objects saved so can make additional tweaks on their properties should such be desired...

Sign in to comment.

Categories

Find more on Data Distribution Plots in Help Center and File Exchange

Community Treasure Hunt

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

Start Hunting!