Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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}
share|improve this question
add comment

1 Answer

up vote 12 down vote accepted

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}
share|improve this answer
    
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
show 7 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.