How do i label each bar in bar group with a "string" on top?

Hi ! Urgent help needed. I have a grouped bar graph and i want to add text on the top of each bar in each group. I am doing this way:
y = [58.1395 62.7907; 40.3900 40.3400]
Y=bar(y)
str={'John'; 'Adam'};
set(gca, 'XTickLabel',str, 'XTick',1:numel(str))
labels= {'Physics', 'Chemistry'; 'Physics', 'Chemistry'};
xt = get(gca, 'XTick');
text(xt, y, labels, 'HorizontalAlignment','center', 'VerticalAlignment','bottom')
Am getting error "error using text Value must be a column or row vector in bar plot" and not able to fix it. Am working in R2015b. Help me.

 Respuesta aceptada

dpb
dpb el 1 de Oct. de 2017
Editada: dpb el 12 de Dic. de 2019
...
hB=bar(y); % use a meaningful variable for a handle array...
hAx=gca; % get a variable for the current axes handle
hAx.XTickLabel=str; % label the ticks
hT=[]; % placeholder for text object handles
for i=1:length(hB) % iterate over number of bar objects
hT=[hT text(hB(i).XData+hB(i).XOffset,hB(i).YData,num2str(hB(i).YData.','%.3f'), ...
'VerticalAlignment','bottom','horizontalalign','center')];
end
The text command does the two groups with the two bars of each group labeled in the one call for each bar group. The x position is that of the data plus the offset and the y position is the data value. The label is formatted to string to be written by num2str; note carefully the transpose operator .' to create a column vector; this is imperative or the two values would be strung together on a single line.
The "trick" is in the hB.XOffset term; the 'XOffset' property is hidden so have to know it exists a priori as there's nothing to give away its existence nor any other data available from which to compute the dX that the routine uses internally for the offset of the bar center from the x location of the group. As noted above, this is a real foopah on TMW's part...
The above results in
ADDENDUM
Specifically addressing the concern that didn't do the labels,
hT=[]; % placeholder for text object handles
for i=1:length(hB) % iterate over number of bar objects
hT=[hT,text(hB(i).XData+hB(i).XOffset,hB(i).YData,labels(:,i), ...
'VerticalAlignment','bottom','horizontalalign','center')];
end
results in
instead...

9 comentarios

thank you for your quick response but
hB=bar(y); % use a meaningful variable for a handle array...
this only works for numeric values, how about the labeling i mentioned to write on each top.
dpb
dpb el 2 de Oct. de 2017
Write whatever you want...generally folks want the numeric values for the bars which is another step beyond just a string. Simply replace the num2str(hB(i).YData.','%.3f') string with a two-vector labels(:,i) as you've defined the array.
Thank you....
dpb
dpb el 2 de Oct. de 2017
No problem...glad to help. Note the error originally about a vector as input for text had to do with how you referenced the labels array without any subscripting which is 2D instead of 1D vector.
The suggested correction of using (:) to cast to the column vector would have fixed that error but you'd have found that you'd written both sets of labels at the same x locations and that would have been at the midpoint between the actual bars, not on the bars themselves. That's one of the "bones of discontent" I have and have had w/ TMW on bar as a general function for years that it just isn't nearly as well-designed as it could/should be.
Hi there
I use the following code in MATLAB R2018a without any problem.
y = [58.1395 62.7907; 40.3900 40.3400];
hB=bar(y); % use a meaningful variable for a handle array...
hAx=gca; % get a variable for the current axes handle
hAx.XTickLabel='str1'; % label the ticks
hT=[]; % placeholder for text object handles
for i=1:length(hB) % iterate over number of bar objects
hT=[hT text(hB(i).XData+hB(i).XOffset,hB(i).YData,num2str(hB(i).YData.','%.3f'), ...
'VerticalAlignment','bottom','horizontalalign','center')];
end
But when I use the above code in MATLAB R2023b, MATLAB shows me the wrong appearance in the figure. I modified the code as follows and it works! I just added pause(.001) after for.
y = [58.1395 62.7907; 40.3900 40.3400];
hB=bar(y); % use a meaningful variable for a handle array...
hAx=gca; % get a variable for the current axes handle
hAx.XTickLabel='str1'; % label the ticks
hT=[]; % placeholder for text object handles
for i=1:length(hB) % iterate over number of bar objects
pause(.001) % inserted becasue error in R2023b
hT=[hT text(hB(i).XData+hB(i).XOffset,hB(i).YData,num2str(hB(i).YData.','%.3f'), ...
'VerticalAlignment','bottom','horizontalalign','center')];
end
Anyone has any Idea about the above error in MATLAB versions?
I think MATLAB developers may have commented on this.
Benjamin Kraus
Benjamin Kraus el 18 de En. de 2024
Editada: Benjamin Kraus el 18 de En. de 2024
@Ali A: The XOffset property is undocumented, so the value is not guaranteed to be accurate at any one particular moment. The reason your code is not working is that the value of XOffset is calculated and used internally, but only after the graphics object has been updated (which happens when you call drawnow). Your pause statement is triggering an update of the graphics object, but drawnow would also work.
However, you really should switch from using the undocumented XOffset property to using the (new in R2019b) documented XEndPoints property instead. Check out this example in the documentation: Specify Labels at the Ends of Bar.
@Benjamin Kraus noted "you really should switch from using the undocumented XOffset property to using the (new in R2019b) documented XEndPoints property"
Yeah, but TMW hid the only visible property available prior to the incorporation of XOffSet leaving users with no alternative other than heuristics or trying to reproduce the internal spacing calculations explicitly, so it's Monday-morning quarterbacking after the fact to criticize its use.
I first submitted formal requests for enhancements including this ability from about Version 11 on and complained about bar() from the inception of the Win 3.1 version.
Why instead of
x = [1 2 3];
vals = [2 3 6; 11 23 26];
b = bar(x,vals);
xtips1 = b(1).XEndPoints;
ytips1 = b(1).YEndPoints;
labels1 = string(b(1).YData);
text(xtips1,ytips1,labels1,'HorizontalAlignment','center',...
'VerticalAlignment','bottom')
...
it isn't something more like
b = bar(x,vals);
b(1).YBarLabel='On','HorizontalAlignment','center','VerticalAlignment','bottom');
...
???
Benjamin Kraus
Benjamin Kraus el 19 de En. de 2024
Editada: Benjamin Kraus el 19 de En. de 2024
@dpb we hear and appreciate your feedback. There is a reason I've been looking at these particular MATLAB Answers posts recently. Keep your eyes open for a future release, and keep the feature requests coming.
dpb
dpb el 19 de En. de 2024
That will be much anticipated, indeed...the only bad part will be that to keep backwards compatibility, probably the only way to fix things will be to introduce a whole new replacement function instead of being able to add onto or replace in situ the existing one; thereby adding to the code bloat issue.
I don't have a solution for that, unfortunately, other than perhaps an introduction of a compatibility flag that would let users choose to orphan functions prior to a given release, but the namespace collisions otherwise probably make such unworkable.

Iniciar sesión para comentar.

Más respuestas (3)

Ben Oeveren
Ben Oeveren el 12 de Sept. de 2018
Editada: dpb el 12 de Sept. de 2018
For those still searching for a solution. Based on previous comments I've used this:
y = grpstats(x,grps,'sum');
ytxt = round(y./repmat(nansum(y,2),1,size(y,2))*100);
ytxt = num2cell(ytxt);
ytxt = cellfun(@(x) [sprintf('%0.1f',x), '%'], ytxt,'un',0);
figure('Position',[107 516 813 182]);
hB = bar(y,'group','EdgeColor','w','BarWidth',1);
hT=[]; % placeholder for text object handles
for i=1:length(hB) % iterate over number of bar objects
hT=[hT,text(hB(i).XData+hB(i).XOffset,hB(i).YData, ytxt(:,i), ...
'VerticalAlignment','bottom','horizontalalign','center')];
end
grid on

1 comentario

dpb
dpb el 12 de Sept. de 2018
Editada: dpb el 12 de Sept. de 2018
NB: Can write the code to produce the strings from the numeric values a little more simply -- don't need to cast to cell array first:
ytxt=cellstr(num2str(ytxt(:),'%0.1f%%'));

Iniciar sesión para comentar.

Image Analyst
Image Analyst el 1 de Oct. de 2017
You can use text() to put text atop each bar. Attached is a demo/example.

4 comentarios

dpb
dpb el 1 de Oct. de 2017
Works for single bar, doesn't work for grouped data owing to the offset of each group from nominal XData position. A MAJOR shortcoming in the bar user interface imo. Doesn't help that the piece of information you need TMW chose to make a hidden property! :(
I believe the text() function will work - you even use it in your solution. Of course my function can be adapted as needed - for example the demo was to show different color bars, which may not be needed and can be skipped. But the essential thing was to use the X and Y values of the bars along with the text() function.
dpb
dpb el 2 de Oct. de 2017
Editada: dpb el 2 de Oct. de 2017
It's not text that's the problem, it's that the x/y position of the data is not where the bar's position is for grouped bar...and TMW doesn't make it easy to find where that position is by hiding the necessary property. :(
And, to be clear, the comment wasn't aimed at you but intended to again highlight the absurdity of the hidden property for what is an obvious need and why there isn't a feature implemented at a much higher level to allow for such simple tasks...Matlab is, after all, supposed to be a "high level" language for rapid development--why is it so much needed to do so much at such a low level to make decent graphics still after some 30 years????
dpb
dpb el 2 de Oct. de 2017
Editada: dpb el 3 de Oct. de 2017
And to amplify further, im(nsh)o < :) >, bar series should have builtin property of label for each bar similar to an axes object 'X|Y|ZTickLabel' with associated properties for positioning it in|outside the bar, horizontal|vertical, etc., etc., etc., ... If just boggles the mind the number of hours folks have spent cumulatively over the years dealing with such trivia instead of actually solving the underlying problem they're working on.

Iniciar sesión para comentar.

Mohammed Abdallatif
Mohammed Abdallatif el 6 de Nov. de 2019
Can someone help to locate the Description (Labels) in the middle of each section of the bar instead of the top? I must change the Y-coordinate of the text, but it needs to be automatically centered and I don't know how.
Code:
text(b(i).XData(n), b(i).get.YEndPoints(n),Labels(n,counter),'HorizontalAlignment','center',...
'VerticalAlignment','bottom');
Unbenannt.png

2 comentarios

dpb
dpb el 6 de Nov. de 2019
You have the positions of each bar section; just compute the means of each section. The first section has an implied lower limit of 0; the bottom of each of the others is the top of the preceding.
thanks @dpb for the idea.
I solved the current problem now using the moving mean function by adding 0 to Origin Ydata:
MovingMeanOfYdata = movmean([0 OriginYdata],[1 0]);

Iniciar sesión para comentar.

Etiquetas

Preguntada:

el 1 de Oct. de 2017

Comentada:

dpb
el 19 de En. de 2024

Community Treasure Hunt

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

Start Hunting!

Translated by