Put Y-Ticks above bars in stacked horizontal bar chart

6 views (last 30 days)
AEdel on 15 Feb 2024
Commented: Adam Danz on 18 Feb 2024
Hello everyone,
I am trying to make a stacked horizontal bar chart. As my y-ticks are rather long strings, I would like to place them above the respective bars. I tried it with the annotation function, but did not succeed. Where am i going wrong? Or is there another way of doing this?
I also want the bars to display the values of each bar (that I have managed)
y = [30 70; 20 80; 60 40];
b = barh(y, 'stacked','BarWidth',0.3);
% set y-axis tick labels
yticks(1:size(y, 1));
yticklabels({'Some very long question', 'Some more text', 'Even more text'})
yPos = 1:size(y, 1);
barValues = sum(y, 2);
for i = 1:length(yPos)
annotation('textbox', [barValues(i) yPos(i)-0.2 0 0], 'String', num2str(barValues(i)),'VerticalAlignment', 'middle', 'HorizontalAlignment', 'left', 'FontSize', 10)
end
% insert values into each bar
for i=1:length(b)
for j = 1:length(b(i).YData)
y = b(i).YEndPoints(j) - (b(i).YData(j)) / 2;
s = sprintf('%.0f', b(i).YData(j));
text(y', b(i).XData(j), s);
end
end
title('The distribution of answers to each question');
Thank you for helping me out.

Austin M. Weber on 16 Feb 2024
Edited: Austin M. Weber on 16 Feb 2024
One option would be to use the text function:
y = [30 70; 20 80; 60 40];
b = barh(y, 'stacked','BarWidth',0.3);
labels = {'Some very long question', 'Some more text', 'Even more text'};
% Add labels above the bars
hold on
text(50, 1.25, labels(1),'HorizontalAlignment','center')
text(50, 2.25, labels(2),'HorizontalAlignment','center')
text(50, 3.25, labels(3),'HorizontalAlignment','center')
hold off
set(gca,'YTick',[]) % Get rid of exterior ytick marks
AEdel on 17 Feb 2024
Thanks, this worked for me.

Adam Danz on 16 Feb 2024
Edited: Adam Danz on 16 Feb 2024
Annotations are tricky because they rely on coordinates that are normalized to the figure area rather than using data units.
If you're adding text at specific locations in axes, use text.
This solution also vectorizes the bar labels instead of using a loop.
y = [30 70; 20 80; 60 40];
b = barh(y, 'stacked','BarWidth',0.3);
xStr = vertcat(b.XData)';
yStr = vertcat(b.YEndPoints)' - y/2;
str = string(y);
text(yStr(:),xStr(:),str(:),...
'HorizontalAlignment','Center',...
'VerticalAlignment','Middle', ...
'Color','w')
ylabs = {'Some very long question', 'Some more text', 'Even more text'};
ylabPos = b(1).XData + b(1).BarWidth/2;
xlabPos = b(1).BaseValue .* ones(size(ylabPos));
text(xlabPos, ylabPos, ylabs, ...
'HorizontalAlignment','left', ...
'VerticalAlignment','bottom')
yticklabels([])
3 CommentsShow 1 older commentHide 1 older comment
Austin M. Weber on 18 Feb 2024
Great question! Programmatically speaking, I would say that Adam's solution is superior because it can be easily applied to charts that have many data bars.
But, if you are creating a bar plot with only 3 bars, my solution takes fewer lines of code and may be easier to read for those who are less experienced with MATLAB syntax.
If you want to plot more than three bars using my solution, you would likely want to wrap the text function in a for-loop. Vectorizing your code (as Adam's solution demonstrates) is technically faster than a for-loop, but for simple plots like this you can decide whether you prefer the simpler solution or the optimized one. Both will work just fine.
Adam Danz on 18 Feb 2024
@AEdel It's great that you're engaging with different solutions and considering the options. Both approaches have their merits..
Regarding my solution, I've leveraged some of the bar properties to generalize the solution. For example, the vertical position of the text is computed using the XData and BarWidth properties instead of hard-coding the vertical position. This way, the solution can be applied to other bar charts that may have different bar widths. The horizontal position of the text is based on the BaseValue value which defines where the bars 'start'.
That said, it's always encouraging to see diverse approaches.