I am trying to generate the following figure more automatically based on this example. Currently, the nodes above and below the bars are generated manually, which I like to avoid. I would prefer to add the text for the upper labels in the datatable and calculate their positions. In the same way, I would like to change the lower values that state the current total.
Also I would like to be able to add another bar in between 4 and 7 that contains the current total (to add emphasis to the current sum like best case, expected case, worst case)
Bonus Question
I tried to use the pgfmathabs
function to have only non-negative values for node near coord
. This is does not work, therefore I am first multiplying and then taking the square root, which seems rather inappropriate.
In order to better explain the desired outcome, I added a picture. I am not sure if the number belows the "step" bars (e.g. "oh no") are needed, if a "full" bar (e.g. "expected case") follows. If another "step" bars follows, they should be there. So if it would be possible to omit the lower numbers, in front of full bars that would be great. (In this minimal example, this does not really make sense, since all number would be ommited but in my full example with 10 "step" bars and 3 "full" bars its a different story.)
MWE
\documentclass[border=5mm, 10pt]{standalone}
\usepackage{pgfplots, pgfplotstable}
\usepackage{filecontents}
\pgfplotsset{compat=1.8}
\begin{filecontents}{datatable.csv}
16
-4
-7
5
\end{filecontents}
\newcommand\barwidthval{22.5pt}%bar width value
\pgfplotstableset{
create on use/accumyprev/.style={
create col/expr={\prevrow{0}+\pgfmathaccuma}
}
}
\makeatletter
\pgfplotsset{
centered nodes near coords/.style={
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,
black,
},
nodes near coords align=center
},
}
\begin{document}
\begin{tikzpicture}
\begin{axis}[
no markers,
axis x line=center,
ybar stacked,
ymin=-20,
point meta=explicit,
centered nodes near coords,
axis lines*=left,
xtick=data,
enlarge x limits=0.05,
enlarge y limits=0.20,
major tick length=0pt,
bar width=\barwidthval,
xticklabels={},
xticklabel style={text width=2cm, align=center},
ytick=\empty,
x axis line style={opacity=0},
y axis line style={opacity=0},
ylabel={Values},
axis on top
]
% 0-x-Axis
\draw ({rel axis cs:0,0}|-{axis cs:0,0}) -- ({rel axis cs:1,0}|-{axis cs:0,0});
% The first plot sets the "baseline": Uses the sum of all previous y values, except for the last bar, where it becomes 0
\addplot +[
y filter/.code={\ifnum\coordindex>2 \def\pgfmathresult{0}\fi},
draw=none,
fill=none
] table [x expr=\coordindex, y=accumyprev] {datatable.csv};
% The values
\addplot +[
fill=orange,
draw=orange,
ybar stacked,
nodes near coords,
nodes near coords={
\pgfkeys{
/pgf/fpu=true, % PGFplots uses floating points internally
}
% pgfmathabs does not work here
\pgfmathparse{sqrt(\pgfplotspointmeta*\pgfplotspointmeta)}\pgfmathprintnumber{\pgfmathresult}
},
] table [x expr=\coordindex, y index=0, meta index=0] {datatable.csv};
% The connecting line. Uses a bit of magic to typeset the ranges
\addplot [const plot, black] table [x expr=\coordindex, y expr=0] {datatable.csv};
\tikzstyle{upper node}=[above, font=\tiny, text width=\barwidthval, align=center, inner xsep=0, inner ysep=1pt,]
\tikzstyle{lower node}=[below,]
\node [upper node] at (axis cs: 0,16) {Best\\ case};
\node [upper node] at (axis cs: 1,16) {Oh no};
\node [lower node] at (axis cs: 1,12) {12};
\node [upper node] at (axis cs: 2,12) {Not again};
\node [lower node] at (axis cs: 2,5) {5};
\node [upper node] at (axis cs: 3,5) {Worst\\ case};
\end{axis}
\end{tikzpicture}
\end{document}
Best Answer
This is a version without pgfplots, but rather with TikZ.
Code
Output
Edit 1: Now with itermittent rest bars and conditionaly skipped labels.
Code
Output
Edit 2: A workaround to remove the label before the last fall is to use
IGNORE
as the second to last rest label:Code
Output
Edit 2: Now with pgfkeys to supply key-value options, which makes customization easier:
Code
Output