[Tex/LaTex] Center nodes near coords in a stacked ybar plot

bar chartnodesnodes-near-coordspgfplotsplot

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},
]
\addplot
coordinates {(1930,50e6) (1940,33e6)
(1950,40e6) (1960,50e6) (1970,70e6)};
\addplot
coordinates {(1930,38e6) (1940,42e6)
(1950,43e6) (1960,45e6) (1970,65e6)};
\addplot
coordinates {(1930,15e6) (1940,12e6)
(1950,13e6) (1960,25e6) (1970,35e6)};
\addplot
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

sample with the nodes on top

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:

before using the values

And after the example I got:

enter image description here

The code is this

\documentclass{standalone}

\usepackage{tikz}
\usepackage{pgfplots}

\pgfplotstableread{
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 [meta index=1] \datatable;
\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
%\addplot table[x index=0,y index=1] \datatable;
%\addplot table[x index=0,y index=2] \datatable;
%\addplot table[x index=0,y index=3] \datatable;
%\addplot table[x index=0,y index=4] \datatable;
%\addplot table[x index=0,y index=5] \datatable;
\legend{Far,Near,Here,There,NotThere}
\end{axis}
\end{tikzpicture}
\end{document}

Best Answer

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


Historical answer:

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}

\pgfplotstableread{
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
]
\addplot table [meta=Far] \datatable;
\addplot table [y=Near, meta=Near] \datatable;
\addplot table [y=Here, meta=Here] \datatable;
\addplot table [y=There, meta=There] \datatable;
\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}

\pgfplotstableread{
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
]
\addplot table [y=1,meta=1] \datatable;
\addplot table [y=2, meta=2] \datatable;
\addplot table [y=3, meta=3] \datatable;
\addplot table [y=4, meta=4] \datatable;
\addplot table [y=5, meta=5] \datatable;
\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}

\pgfplotstableread{
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=1,meta=1] \datatable;
\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}