# Center nodes near coords in a stacked ybar plot

I'm trying to center the node values, at the middle of each bar, in a stacked plot. However I can only put those nodes in the top of each bar. Is it possible to move those down? To the center of each bar to be more precise.

\documentclass{article}

\usepackage{tikz}
\usepackage{pgfplots}

\begin{document}
\begin{tikzpicture}
\begin{axis}[
x tick label style={
/pgf/number format/1000 sep=},
ylabel=Population,
enlargelimits=0.05,
legend style={at={(0.5,-0.15)},
anchor=north,legend columns=-1},
ybar stacked,
nodes near coords, nodes near coords align={center},
]
coordinates {(1930,50e6) (1940,33e6)
(1950,40e6) (1960,50e6) (1970,70e6)};
coordinates {(1930,38e6) (1940,42e6)
(1950,43e6) (1960,45e6) (1970,65e6)};
coordinates {(1930,15e6) (1940,12e6)
(1950,13e6) (1960,25e6) (1970,35e6)};
coordinates {(1930,15e6) (1940,12e6)
(1950,13e6) (1960,25e6) (1970,35e6)};

\legend{Far,Near,Here,There}
\end{axis}
\end{tikzpicture}
\end{document}


So this is what I got

Edit

Ok, thanks to Jake now the example above works. I also notice that my example using floats wasn't working because I didn't use the \makeatletter after the document.

However, when I'm using float values using the solution the bars don't appear as they are when not using them.

Before using the values:

And after the example I got:

The code is this

\documentclass{standalone}

\usepackage{tikz}
\usepackage{pgfplots}

1   19.178  26.027  8.219   6.849   39.726
2   54.795  21.918  4.110   6.849   12.329
3   28.767  16.438  6.849   8.219   39.726
4   63.014  2.740   2.740   2.740   28.767
5   90.411  1.370   6.849   0.000   1.370
6   15.068  2.740   16.438  8.219   57.534
7   67.123  0.000   0.000   0.000   32.877
8   72.603  6.849   5.479   0.000   15.068
9   56.164  12.329  6.849   4.110   20.548
10  50.685  4.110   8.219   1.370   35.616
}\datatable

\begin{document}
\makeatletter
\begin{tikzpicture}
\begin{axis}[
ylabel=label,
xtick=data,
xticklabels={S1,S2,S3,S4,S5,S6,S7,S8,S9,S10},
enlarge y limits=false,
enlarge x limits=0.1,
ymin=0,ymax=100,
ybar stacked,
bar width=10pt,
legend style={
font=\footnotesize,
cells={anchor=west},
legend columns=5,
at={(0.5,-0.15)},
anchor=north,
/tikz/every even column/.append style={column sep=0.2cm}
},
% Comment from here to produce the first plot
point meta=explicit,
calculate offset/.code={
\pgfkeys{/pgf/fpu=true,/pgf/fpu/output format=fixed}
\pgfmathsetmacro\testmacro{(\pgfplotspointmeta*10^\pgfplots@data@scale@trafo@EXPONENT@y)/2*\pgfplots@y@veclength)}
\pgfkeys{/pgf/fpu=false}
},
every node near coord/.style={
/pgfplots/calculate offset,
yshift=-\testmacro
},
nodes near coords,
nodes near coords align=center
]
\addplot table [y=1, meta index=2] \datatable;
\addplot table [y=2, meta index=3] \datatable;
\addplot table [y=3, meta index=4] \datatable;
\addplot table [y=4, meta index=5] \datatable;
% This code produces the first plot
\legend{Far,Near,Here,There,NotThere}
\end{axis}
\end{tikzpicture}
\end{document}

-

This manual solution is no longer needed

As Stefan Pinnow points out, from version 1.9 onward, PGFPlots can do this out of the box

Alright, here it is! Two key things: You need to provide the meta data explicitly by saying meta=<column name> in your \addplot commands, otherwise PGFplots will use the y value, which is the sum of the individual values. Then, you need to convert the data value into a distance on the canvas. The formula for the label offset is <data value> * 10^<scaling exponent> / 2 * <y vector length>. Because of the large values involved in your example, this requires the fpu library to be activated before the calculation, and switched off afterwards.

\documentclass{standalone}

\usepackage{tikz}
\usepackage{pgfplots}

Year    Far     Near    Here    There
1930    50e6    38e6    15e6    15e6
1940    33e6    42e6    12e6    12e6
1950    40e6    43e6    13e6    13e6
1960    50e6    45e6    25e6    25e6
1970    70e6    65e6    35e6    35e6
}\datatable

\begin{document}
\makeatletter
\begin{tikzpicture}
\begin{axis}[
ymin=0,
x tick label style={
/pgf/number format/1000 sep={}
},
ylabel=Population,
enlargelimits=0.14, enlarge y limits=upper,
legend style={
at={(0.5,-0.15)},
anchor=north,
legend columns=-1
},
ybar stacked, bar width=3.5em,
point meta=explicit,
calculate offset/.code={
\pgfkeys{/pgf/fpu=true,/pgf/fpu/output format=fixed}
\pgfmathsetmacro\testmacro{(\pgfplotspointmeta*10^\pgfplots@data@scale@trafo@EXPONENT@y)/2*\pgfplots@y@veclength)}
\pgfkeys{/pgf/fpu=false}
},
every node near coord/.style={
/pgfplots/calculate offset,
yshift=-\testmacro
},
nodes near coords,
nodes near coords align=center
]
\legend{Far,Near,Here,There}
\end{axis}
\end{tikzpicture}
\end{document}


Your second example works as well if you supply the correct y=<...>, meta=<...> options. They need to be the same, and they need to correspond to the correct column in your datatable.

In the example below, I've introduced a new style nodes near coords vertically centered, which performs the calculations and shifts the labels accordingly.

The topmost segments of the S2 and S4 row do not have labels because your data adds up to slightly more than 100, so the labels are trimmed off. The easiest way to fix this is to set ymax=100.01. Alternatively, you could make sure your data adds up to exactly 100.

To hide the labels where the data is 0, you can use the \pgfmathfloatifflags{<float to check>}{<flag>}{<action if true>}{<action if false>} macro to turn the node into a coordinate (which doesn't have a text) if the value is 0.

\documentclass[border=5mm]{standalone}

\usepackage{tikz}
\usepackage{pgfplots}

1   19.178  26.027  8.219   6.849   39.726
2   54.794  21.918  4.110   6.849   12.329
3   28.767  16.438  6.849   8.219   39.726
4   63.014  2.740   2.740   2.740   28.767
5   90.411  1.370   6.849   0.000   1.370
6   15.068  2.740   16.438  8.219   57.534
7   67.123  0.000   0.000   0.000   32.877
8   72.603  6.849   5.479   0.000   15.068
9   56.164  12.329  6.849   4.110   20.548
10  50.685  4.110   8.219   1.370   35.616
}\datatable

\makeatletter
\pgfplotsset{
calculate offset/.code={
\pgfkeys{/pgf/fpu=true,/pgf/fpu/output format=fixed}
\pgfmathsetmacro\testmacro{(\pgfplotspointmeta*10^\pgfplots@data@scale@trafo@EXPONENT@y)/2*\pgfplots@y@veclength)}
\pgfkeys{/pgf/fpu=false}
},
nodes near coords vertically centered/.style={
every node near coord/.append style={
/pgfplots/calculate offset,
yshift=-\testmacro
},
nodes near coords align=center
}
}
\makeatother

\begin{document}

\begin{tikzpicture}
\begin{axis}[
xtick=data,
xticklabels={S1,S2,S3,S4,S5,S6,S7,S8,S9,S10},
enlarge y limits=false,
enlarge x limits=0.1,
ymin=0,ymax=100.01,
ybar stacked,
bar width=10pt,
point meta=explicit,
nodes near coords={\pgfmathprintnumber[fixed zerofill,precision=1]{\pgfplotspointmeta}
},
every node near coord/.style={
check for zero/.code={ % If meta=0, make the node a coordinate (which doesn't have text)
\pgfmathfloatifflags{\pgfplotspointmeta}{0}{
\pgfkeys{/tikz/coordinate}
}{}
}, check for zero, fill=white, fill opacity=0.75, text opacity=1, font=\scriptsize, inner ysep=0.5pt},
nodes near coords vertically centered
]
\end{axis}
\end{tikzpicture}
\end{document}


With a couple of changes, the code can also be used for xbar charts:

\documentclass[border=5mm]{standalone}

\usepackage{tikz}
\usepackage{pgfplots}

1   19.178  26.027  8.219   6.849   39.726
2   54.794  21.918  4.110   6.849   12.329
3   28.767  16.438  6.849   8.219   39.726
4   63.014  2.740   2.740   2.740   28.767
5   90.411  1.370   6.849   0.000   1.370
6   15.068  2.740   16.438  8.219   57.534
7   67.123  0.000   0.000   0.000   32.877
8   72.603  6.849   5.479   0.000   15.068
9   56.164  12.329  6.849   4.110   20.548
10  50.685  4.110   8.219   1.370   35.616
}\datatable

\makeatletter
\pgfplotsset{
calculate offset/.code={
\pgfkeys{/pgf/fpu=true,/pgf/fpu/output format=fixed}
\pgfmathsetmacro\testmacro{(\pgfplotspointmeta*10^\pgfplots@data@scale@trafo@EXPONENT@x)/2*\pgfplots@x@veclength)}
\pgfkeys{/pgf/fpu=false}
},
nodes near coords horizontally centered/.style={
every node near coord/.append style={
/pgfplots/calculate offset,
xshift=-\testmacro
},
nodes near coords align=center
}
}
\makeatother

\begin{document}

\begin{tikzpicture}
\begin{axis}[
ytick=data,
yticklabels={S1,S2,S3,S4,S5,S6,S7,S8,S9,S10},
enlarge y limits=false,
enlarge y limits=0.1,
xmin=0,
xbar stacked,
bar width=10pt,
point meta=explicit,
nodes near coords={\pgfmathprintnumber[fixed zerofill,precision=0]{\pgfplotspointmeta}
},
every node near coord/.style={
check for zero/.code={ % If meta=0, make the node a coordinate (which doesn't have text)
\pgfmathfloatifflags{\pgfplotspointmeta}{0}{
\pgfkeys{/tikz/coordinate}
}{}
}, check for zero, fill=white, fill opacity=0.75, text opacity=1, font=\scriptsize, inner ysep=0.5pt, inner xsep=0.5pt},
nodes near coords horizontally centered
]
\addplot table [y=0, x=2, meta=2] \datatable;
\addplot table [y=0, x=3, meta=3] \datatable;
\addplot table [y=0, x=4, meta=4] \datatable;
\addplot table [y=0, x=5, meta=5] \datatable;
\end{axis}
\end{tikzpicture}
\end{document}

-
Thanks for your solution. It is a huge advance. However, I'm trying to do the same for other graphs, which uses a different type of data, not necessarily exponential. I'm trying to change your code for make it work for decimal point but I keep getting errors about the exponential. Can you expand your answer, so it can be use with any type of data. – adn Nov 29 '11 at 2:21
This should work with any dataset. If the numbers are small, the \pgfplots@data@scale@trafo@EXPONENT@y will be set to 0, so nothing gets scaled. I changed the values in the datatable to 50, 33, etc. (instead of 50e6, 33e6...), and it works fine. Could you provide an example where the solution fails? – Jake Nov 29 '11 at 2:24
I check and the float values weren't working due to the \makeatletter command that I wasn't including. My bad, sorry. However, after that I don't understand why the plot is moved when using the floats. I update my answer with this issue and the code. – adn Nov 29 '11 at 3:00
My bad, you are right. I feel so dumb, not seeing the meta part. Sorry, and thanks for the help. :) – adn Nov 29 '11 at 8:09
Sorry, to ask again. Why does the values in S2 and S4 do not appear? Also, is there a way to eliminate the zeros from the labels?, as the bar is not shown. – adn Nov 29 '11 at 8:38

For the record

In PGFPlots v1.9 Jake's solution has been implemented as default. To use it, just add \pgfplotsset={compat=1.9} or higher to the preamble. In the following code I also scaled the nodes near coords values so that they fit the scaling of the y axis.

\documentclass[border=2mm]{standalone}
\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{
compat=1.9,
}
\begin{document}
\begin{tikzpicture}
\begin{axis}[
ymin=0,
x tick label style={
/pgf/number format/1000 sep=,
},
ylabel=Population,
enlarge x limits=0.1,
legend style={
at={(0.5,-0.15)},
anchor=north,
legend columns=-1,
},
ybar stacked,
%
%            % only show nodes near coords' without scaling the values
%            nodes near coords,
% apply the same scale to nodes near coords' as for the y axis
visualization depends on={rawy/1e8 \as \scaledy},
nodes near coords={%
\pgfmathprintnumber{\scaledy}%
},
% also increase the bar width' a bit, so the numbers fit into
% the bars
bar width=5,
]
(1930,50e6) (1940,33e6) (1950,40e6) (1960,50e6) (1970,70e6)
};
(1930,38e6) (1940,42e6) (1950,43e6) (1960,45e6) (1970,65e6)
};
(1930,15e6) (1940,12e6) (1950,13e6) (1960,25e6) (1970,35e6)
};
(1930,15e6) (1940,12e6) (1950,13e6) (1960,25e6) (1970,35e6)
};

\legend{
Far,
Near,
Here,
There%
}
\end{axis}
\end{tikzpicture}
\end{document}
`

-