1

I created an environment for bar charts based on values from 0-100.

In my current setup, data is read from a file and parsed into a single \addplot. In the real world, I will need to illustrate a comparison between planned vs. current percentages. To illustrate this difference aesthetically, I will need separate colors, which, from my understanding, means separate \addplot macros.

UPDATE: My approach is nonsense.

My approach is to duplicate the environment horizontalbarchart shown in the example code and create another macro called horizontaldualbarchart. This environment will use two \addplot calls:

Filtering

  • First \addplot filters out even row indexes of \jobname-barchart.export parsed and saved as macro \datatable by \pgfplotstableread
  • Second \addplot filters out odd row indexes of \jobname-barchart.export parsed and saved as macro \datatable by \pgfplotstableread

Formatting

  • every even row index is an \addplot with the color orange.
  • every odd row index is an \addplot with the color blue!65!black.

Pseudo Code

I need something like x index filter/.code= to filter the table values by row index. I think the bars get aligned the names of the `yticklabels are the same when the data is read again. I am not sure how this works (yet) under the hood.

\tikzset{style-addplot-color1/.style={draw=none,fill=blue!65!black}}% addplot bar format
\tikzset{style-addplot-color2/.style={draw=none,fill=orange}}% addplot bar format
\newenvironment{horizontaldualbarchart}
  {\VerbatimOut{\jobname-barchart.export}}
  {\endVerbatimOut
  % Inject BODY into pgfplotstableread
 \pgfplotstableread[col sep=comma, header=false]{\jobname-barchart.export}{\datatable}
   % Use \datatable for plot data
 \begin{tikzpicture}
   \begin{axis}[horizontalbarchartstyle]
     \addplot [style-addplot-color1] table [col sep=comma, y expr=\coordindex, x=0, x index filter/.code{\ifeven\numexpr\coordindex\relax\def\pgfmathresult{}\fi] {\datatable};
     \addplot [style-addplot-color2] table [col sep=comma, y expr=\coordindex, x=0, x index filter/.code{\ifodd\numexpr\coordindex\relax\def\pgfmathresult{}\fi}] {\datatable};
   \end{axis}
 \end{tikzpicture}
  }%

Pseudo Output

This is how it would look if I just added another \addplot with a different style definition, but that reads all of the same rows (no filtering).

enter image description here

Example Code:

\documentclass{article}
\usepackage{fontspec}
\usepackage{pgfplots,pgfplotstable}
\usepackage{fancyvrb}

\pgfplotsset{horizontalbarchartstyle/.style={
 % symbolic y coords=,% set dict of expected textual y coordinates, we avoid this dup of data by using "yticklabels from table" and "ytick=data"
  axis lines*=left,
  y=10mm,% vertical spacing (define the length of a unit in the y direction )
  xbar,
  bar width=5mm,% bar thickness
  y axis line style = { opacity = 0 },
  x axis line style = { black!35 },
  width=.6\textwidth,% width of chart itself, does not include tick labels
%  xmajorgrids,
  xminorgrids,
  xlabel={}, % optional label below x axis but useless in global style
  xmin=0,
  xmax=100,
  xtick={0,100,...,100},
  minor xtick={25,50,...,75},
  point meta={x},
  nodes near coords={\color{blue!65!black}\pgfmathprintnumber[assume math mode=true]{\pgfplotspointmeta}\%},% puts text (set in "point meta" key) near coordinates.
  every node near coord/.style={font=\bfseries},
  nodes near coords align={horizontal},% alignment of "nodes near coords"
  enlarge y limits={abs=10mm},% add space above and below bars
  yticklabels from table={\datatable}{1}, % necessary for pgfplotstableread data
  ytick=data,
  xticklabel={\pgfmathprintnumber[assume math mode=true]{\tick}\%}, % disable math mode
  x tick label style={font=\footnotesize,black!35},
%  x tick style={opacity=0},
  y tick style={opacity=0},
  y tick label style={font=\raggedright\small,color=orange,align=right,text width=.4\textwidth},
  legend style={font=\footnotesize},
  label style={font=\footnotesize},
  minor grid style={dotted,black!65},
  major grid style={dashed,orange},
  },
 }
\tikzset{style-addplot-color1/.style={draw=none,fill=blue!65!black}}% bar format

\newenvironment{horizontalbarchart}
  {\VerbatimOut{\jobname-barchart.export}}
  {\endVerbatimOut
  % Inject BODY into pgfplotstableread
 \pgfplotstableread[col sep=comma, header=false]{\jobname-barchart.export}{\datatable}
   % Use \datatable for plot data
 \begin{tikzpicture}
   \begin{axis}[horizontalbarchartstyle]
     \addplot [style-addplot-color1] table [col sep=comma, y expr=\coordindex, x=0] {\datatable};
   \end{axis}
 \end{tikzpicture}
  }%


\begin{document}

\begin{horizontalbarchart}
10, Ans
20, Zwåa
30, Drui
40, Vieri
50, Fünfi
\end{horizontalbarchart}

\end{document}
  • You can't just have two data columns, one for planned, one for current? – Torbjørn T. Jun 16 '17 at 10:19
  • @TorbjørnT. Perhaps that would be a better approach. My only requirement there is to keep horizontaldualbarchart data super clean: start env, raw data, end env. Anyway, I am not sure how to add another column, because column 1 = x and column 2 = y. I think you are suggesting that I make two \pgfplotstableread calls. I suppose that would be the point at which I specify x and y. – Jonathan Komar Jun 16 '17 at 10:33
  • No I was not suggesting two \pgfplotstablereads, I was suggesting one table with three columns, see my answer. – Torbjørn T. Jun 16 '17 at 10:40
  • @TorbjørnT. Yea, I got the three columns part. The key point in your answer is to set the x index. Very good. Thanks. – Jonathan Komar Jun 16 '17 at 10:42
2

Just add another column in the data, and set the appropriate x index for the two \addplots. Note I set xbar={1pt} to reduce the spacing, and reduced the bar width to 4mm. Note also that your setting text width=.4\textwidth extends the bounding box of the tikzpicture by a lot, so unless your labels are very long, I would remove that.

enter image description here

\documentclass{article}
\usepackage{fontspec}
\usepackage{pgfplots,pgfplotstable}
\usepackage{fancyvrb}

\pgfplotsset{horizontalbarchartstyle/.style={
 % symbolic y coords=,% set dict of expected textual y coordinates, we avoid this dup of data by using "yticklabels from table" and "ytick=data"
  axis lines*=left,
  y=10mm,% vertical spacing (define the length of a unit in the y direction )
  xbar={1pt},
  bar width=4mm,% bar thickness
  y axis line style = { opacity = 0 },
  x axis line style = { black!35 },
  width=.6\textwidth,% width of chart itself, does not include tick labels
%  xmajorgrids,
  xminorgrids,
  xlabel={}, % optional label below x axis but useless in global style
  xmin=0,
  xmax=100,
  xtick={0,100,...,100},
  minor xtick={25,50,...,75},
  point meta={x},
  nodes near coords={\color{blue!65!black}\pgfmathprintnumber[assume math mode=true]{\pgfplotspointmeta}\%},% puts text (set in "point meta" key) near coordinates.
  every node near coord/.append style={font=\bfseries},
  nodes near coords align={horizontal},% alignment of "nodes near coords"
  enlarge y limits={abs=10mm},% add space above and below bars
  yticklabels from table={\datatable}{2}, % necessary for pgfplotstableread data
  ytick=data,
  xticklabel={\pgfmathprintnumber[assume math mode=true]{\tick}\%}, % disable math mode
  x tick label style={font=\footnotesize,black!35},
%  x tick style={opacity=0},
  y tick style={opacity=0},
  y tick label style={font=\raggedright\small,color=orange,align=right,text width=.4\textwidth},
  legend style={font=\footnotesize},
  label style={font=\footnotesize},
  minor grid style={dotted,black!65},
  major grid style={dashed,orange},
  },
 }
\tikzset{
  style-addplot-color1/.style={draw=none,fill=blue!65!black},
  style-addplot-color2/.style={draw=none,fill=orange},
}% bar format

\newenvironment{horizontaldualbarchart}
  {\VerbatimOut{\jobname-barchart.export}}
  {\endVerbatimOut
  % Inject BODY into pgfplotstableread
 \pgfplotstableread[col sep=comma, header=false]{\jobname-barchart.export}{\datatable}
   % Use \datatable for plot data
 \begin{tikzpicture}
   \begin{axis}[horizontalbarchartstyle]
     \addplot [style-addplot-color1] table [col sep=comma, y expr=\coordindex, x index=0] {\datatable};
     \addplot [style-addplot-color2] table [col sep=comma, y expr=\coordindex, x index=1] {\datatable};
   \end{axis}
 \end{tikzpicture}
  }%


\begin{document}

\begin{horizontaldualbarchart}
9, 10, Ans
15, 20, Zwåa
35, 30, Drui
30, 40, Vieri
42, 50, Fünfi
\end{horizontaldualbarchart}

\end{document}
  • My labels are very long ;) That is why I have the crazy big bounding box. When you have many of these things stacked on top of each other, the bounding boxes will be aligned horizontally regardless of label length so that a visual comparison between bar charts is possible. (otherwise 0% and 100% are all over the horizontal plane based on the label length--not good) – Jonathan Komar Jun 16 '17 at 10:44
  • 1
    @JonathanKomar Ah yes, that is a point. By the way, if you want the nodes near coords in the same color as the bar, move nodes near coords={<color><text>} to the options of the \addplot, i.e. \addplot [style1,nodes near coords={...}] .. – Torbjørn T. Jun 16 '17 at 10:48
  • How did you get the percentages in nodes near coords to align with the bars? – Jonathan Komar Jun 16 '17 at 10:59
  • @JonathanKomar Right, forgot to mention that. Just changed from every node near coord/.style to every node near coord/.append style. Haven't looked up exactly what is in that style, but appending to it instead of overwriting it fixed that bit. – Torbjørn T. Jun 16 '17 at 11:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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