7

I strive to make a bar chart displaying opinions for and against different topics.

fictive example poll

I tried to do this with pgfplots starting from bar and interval bar charts. Bars to both sides can be archived using negative values and the option forget plot. The major remaining problem is splitting the x axis so that the labels are in between the negative and positive bars. Automatic line breaking of long labels would be a nice extra feature.

The image mockup above I made using TikZ. Here is the code, perhaps this inspires anyone.

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning, fit, snakes, scopes}

\begin{document}
\begin{tikzpicture}
  { [align=center]  % labels
    \node (a) at (0, 0) {short l.};
    \node (b) at (0, 1) {medium labels};
    \node (c) at (0, 2) {very enormously \\ excessively long labels};
  }

  % invisible node surrounding all labels
  \node [fit={(a) (b) (c)}] (center) {};

  % axis labels
  \node (title) at (0, -1.5) {Opinions};
  \node at (3, -1) {likes};
  \node at (-3, -1) {dislikes};

  { [yshift=-.25cm]  % named coordinates below as anchors
    \coordinate (east) at (center.south east) {};
    \coordinate (west) at (center.south west) {};
  }

  { [minimum height=5ex]  % bars
    { [every node/.style={fill=teal, anchor=west}]  % positives
      \node [minimum width=2cm] at (a-|east) {};
      \node [minimum width=6cm] at (b-|east) {};
      \node [minimum width=4cm] at (c-|east) {};
    }
    { [every node/.style={fill=purple, anchor=east}]   % negatives
      \node [minimum width=4cm] at (a-|west) {};
      \node [minimum width=3cm] at (b-|west) {};
      \node [minimum width=1cm] at (c-|west) {};
    }
  }

  % axes
  \draw[->] (east) -- ++(6.25, 0);
  \draw[snake=ticks,segment length=1cm, segment amplitude=1.75] (east) -- ++(6, 0);
  \draw[->] (west) -- ++(-4.25, 0);
  \draw[snake=ticks,segment length=1cm, segment amplitude=1.75] (west) -- ++(-4, 0);

  { [every node/.style={yshift=-2ex}] % axis labels
    \node at (east) {0};
    \node[xshift=2cm] at (east) {2};
    \node[xshift=4cm] at (east) {4};
    \node[xshift=6cm] at (east) {6};
    \node at (west) {0};
    \node[xshift=-2cm] at (west) {2};
    \node[xshift=-4cm] at (west) {4};
  }
\end{tikzpicture}
\end{document}

Can such a chart be made with pgfplots? Or is there another package that can do it?

  • Wouldn't it be easier to compare the length of the bars corresponding to the different opinions if the bars were stacked? Have a look at the pgfplots package. It provides high-level support for such bar graphs. (No need to draw the bars with low level TikZ.) – user10274 Oct 7 '13 at 15:51
  • 1
    Have a look at the presentation about creating data plots on my LaTeX and Friends site. Look for creating paired bar graphs. – user10274 Oct 7 '13 at 16:00
  • In this dass, the relative differences between the items are more important than the difference between positives and negatives. So a stacked or interleaved plot would be less optimal. – XZS Oct 10 '13 at 14:52
  • If the relative difference are more important, this is exactly why you should (IMHO) use a stacked barchart. With such charts it's immediately clear when there's a difference. With your charts, the reader will have to look from left to right and back again a few times if they want to determine the difference (if there is one). – user10274 Oct 11 '13 at 8:24
9

Whit pgfplots you can use something like this (not so automatized but still a solution)

\documentclass{article}
\usepackage{pgfplots}

\begin{document}
\begin{tikzpicture}
\begin{axis}[
name=like,
scale only axis,
xbar, xmin=0, xmax=40,
%xbar=2pt,
xlabel={likes},
width=5cm, height= 3cm,
ytick={1,2,3,4,5},
yticklabels={sa,safda,afa,afa,af},
y tick label style={text width=3cm,align=center},
axis x line=left,
axis y line=none,
clip=false
]
\addplot[green,fill=green] coordinates {
    (36,1) 
    (17,2) 
    (26,3)};
\node[xshift=-1.5cm,align=center] at (axis cs:0,3) {very enormously \\ excessively long\\ labels}; 
\node[xshift=-1.5cm,align=center] at (axis cs:0,2) {medium labels};
\node[xshift=-1.5cm,align=center] at (axis cs:0,1) {short l};
\end{axis}
\begin{axis}[ 
at={(like.north west)},anchor=north east, xshift=-3cm,
scale only axis,
xbar, xmin=0,xmax=40,
xlabel={dislikes},
ytick={1,2,3,4,5},
yticklabels={},
width=5cm, height= 3cm,
 x dir=reverse,
axis x line=left,
axis y line=none,
]
\addplot[red,fill=red] coordinates {
    (7,1) 
    (30,2) 
    (14,3)};
\end{axis}
\end{tikzpicture}
\end{document}

enter image description here

  • I like the strategy to append an inverted axis on the side, especially because this allows for dissymetric graphs with different ymax values. But in fact, I read all data with a pgfplots table, also the x tick labels. They could be iterated with \foreach, but it would be more elegant to use "x tick labels from table" and attach the left axis besides the longest label. Which anchor can be used for this alignment? – XZS Oct 10 '13 at 15:06
6

Here is my solution using tikz. Some things are automated, for example, making a bar is as simple as typing \like{1}{2cm}; where like (or dislike) is the name of the command, {1} refers to the "height" of the bar, 1 is the lowest, 2 would be the second bar above it and so on. The last one says how much the bar will extend. The number corresponds to the actual node on the chart (2cm = up to node 2).

bar chart

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning, fit, snakes, scopes}

\tikzset{
  label/.style={align=center,text width=4.5cm,inner sep=1mm,outer sep=0mm,font=\footnotesize},
  }

\newcommand*{\like}[2]{
        \node[fill=teal, anchor=west, xshift=2cm, minimum width={#2}, minimum height=8mm] at (0,{#1}) {};
}

\newcommand*{\dislike}[2]{
        \node[fill=purple, anchor=east, xshift=-2cm, minimum width={#2}, minimum height=8mm] at (0,{#1}) {};
}

\begin{document}
\begin{tikzpicture}[y=1cm]

    \draw[|->, -latex, draw] (2,0) -- (10,0)  node[align=center,midway,yshift=-1cm]{Like};
    \draw[|->, -latex, draw] (-2,0) -- (-10,0)node[align=center,midway,yshift=-1cm]{Dislike};

        \foreach \x [evaluate=\x as \degree using int(0+\x)] in {0,2,...,6}{ 
            \draw (\x,0) node[below=7pt,anchor=north,xshift=2cm,font=\scriptsize] {$\degree$}; 
            \draw[xshift=2cm] (\x,-0.1) -- (\x,0.1); \draw[xshift=2cm] (\x+1,0) -- (\x+1,0.1);
            }

        \foreach \x [evaluate=\x as \degree using int(0-\x)] in {0,-2,...,-6}{ 
            \draw (\x,0) node[below=7pt,anchor=north,xshift=-2cm,font=\scriptsize] {$\degree$}; 
            \draw[xshift=-2cm] (\x,-0.1) -- (\x,0.1); \draw[xshift=-2cm] (\x-1,0) -- (\x-1,0.1);
            }
% labels

\node[label] at (0, 1) {short l.};
\node[label] at (0, 2) {medium labels};
\node[label] at (0, 3) {very very very enormously excessively long labels};

% bars      

\like{1}{2cm};
\like{2}{6cm};
\like{3}{4cm};

\dislike{1}{4cm};
\dislike{2}{5cm};
\dislike{3}{2cm};

\end{tikzpicture}
\end{document}

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.