Center nodes near coords in a stacked ybar plot
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}
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,
]
\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}