3

I have a data table which contains positive and negative values for different categories (columns) as input. I want to visualize these values by a stacked bar plot, which shows a stacked bar plot for all negative values to the left and all positive values as a stacked bar to the right. I already found out that I cannot do this with a single axis environment, as xbar stacked does not render correctly (at least not as I want it) if positive and negative values are added to the same stack. Therefore, I am trying to apply a strategy that I found here: stacking negative value for ybar, which uses two axis environments with the same range for the x-axes. I started with the positive bars by filtering out negative values read from the data table. This works as expected, as long as I do not specify xmin and xmax for that axis. If I do so, the output is looking weird and not explainable for me.

Here is a simple example of my current tries:

\documentclass[border=5mm]{standalone}

\usepackage{pgfplots}
\usepackage{pgfplotstable}

\pgfplotstableread{
Year    Cat1  Cat2    Cat3   Cat4
2005    10     50      -10     30  
2006    -40     60      -15     90
2007    -20     60      -15     60
}\mytable

\begin{document}%

  % First try: use negative and positive values in the same axis environment - FAIL
  \begin{tikzpicture}
    \begin{axis}[
      xbar stacked,
      ]
      \addplot table [x index=1,y expr=\coordindex] {\mytable};
      \addplot table [x index=2,y expr=\coordindex] {\mytable};
      \addplot table [x index=3,y expr=\coordindex] {\mytable};
      \addplot table [x index=4,y expr=\coordindex] {\mytable};
      \legend{Cat1,Cat2,Cat3,Cat4}
    \end{axis}
  \end{tikzpicture}

  % Second try: only draw positive values into single axis environment - SUCCESS, but drawing of negative values is missing
  \begin{tikzpicture}
    \begin{axis}[
      xbar stacked,
      restrict x to domain*=0:,
      ]
      \addplot table [x index=1,y expr=\coordindex] {\mytable};
      \addplot table [x index=2,y expr=\coordindex] {\mytable};
      \addplot table [x index=3,y expr=\coordindex] {\mytable};
      \addplot table [x index=4,y expr=\coordindex] {\mytable};
      \legend{Cat1,Cat2,Cat3,Cat4}
    \end{axis}
  \end{tikzpicture}

  % Third try: explicitly defining the x-axis range to center the zero point - FAILS
  \begin{tikzpicture}
    \begin{axis}[
      xbar stacked,
      restrict x to domain*=0:,
      xmin=-200, xmax=200,  % <-- only difference to second try (above)
      ]
      \addplot table [x index=1,y expr=\coordindex] {\mytable};
      \addplot table [x index=2,y expr=\coordindex] {\mytable};
      \addplot table [x index=3,y expr=\coordindex] {\mytable};
      \addplot table [x index=4,y expr=\coordindex] {\mytable};
      \legend{Cat1,Cat2,Cat3,Cat4}
    \end{axis}
  \end{tikzpicture}

\end{document}

This piece of code results in the following output: Result of the above LaTeX code

Does anybody have a working solution for this problem or at least an explanation why the definition of xmin and xmax changes the produced output and does not only define the axis range (as the command implies)?

3 Answers 3

5

In PGFPlots v1.13 your example now works with the new key stack negative=separate. Here the full code, which can also be found in the manual in the section 4.5.9 on page 99.

\documentclass[border=5mm]{standalone}
\usepackage{pgfplots}
    \pgfplotsset{compat=1.13}
\begin{document}
    \begin{tikzpicture}
        \pgfplotstableread{
            Year Cat1 Cat2 Cat3 Cat4
            2005 10 50 -10 30
            2006 -40 60 -15 90
            2007 -20 60 -15 60
        }\mytable
        \begin{axis}[
            xbar stacked,
            % is default anyway:
            stack negative=separate,
            %
            /pgf/number format/1000 sep=,
            xmajorgrids,
            nodes near coords,
            nodes near coords style={font=\tiny},
            ytick distance=1,
            legend style={
                at={(0.5,-0.1)},
                anchor=north,legend
                columns=-1,
            },
            extra x ticks={0},
            extra x tick style={
                grid style={black},
                xticklabel=\empty,
            },
        ]
            \addplot table [x index=1,y=Year] {\mytable};
            \addplot table [x index=2,y=Year] {\mytable};
            \addplot table [x index=3,y=Year] {\mytable};
            \addplot table [x index=4,y=Year] {\mytable};
            \legend{Cat1,Cat2,Cat3,Cat4}
        \end{axis}
    \end{tikzpicture}
\end{document}

image showing result of above code

4

Maybe you can use a groupplot with horizontal sep=0pt to get

enter image description here

Code:

\documentclass[border=5mm]{standalone}
\usepackage{pgfplots}
\usepackage{pgfplotstable}
\usepgfplotslibrary{groupplots}
\pgfplotsset{compat=1.10}

\pgfplotstableread{
Year    Cat1  Cat2    Cat3   Cat4
2005    10     50      -10     30  
2006    -40     60      -15     90
2007    -20     60      -15     60
}\mytable

\begin{document}%
\begin{tikzpicture}
  \begin{groupplot}[
      group style={
        group size = 2 by 1,
        horizontal sep=0pt,
        yticklabels at=edge left,
        },
      xbar stacked,
      ytick=data,
      yticklabel style={/pgf/number format/1000 sep=},
      scale only axis,
      height=5.7cm,
      width=3.7cm
    ]
    \nextgroupplot[
        xmin=-200,xmax=0,
        xtick={-200,-100},
        axis y line*=left,
        restrict x to domain*=:0,
      ]
      \pgfplotsinvokeforeach{1,...,4}{%
        \addplot table [x index=#1,y index=0] {\mytable};%
      }
    \nextgroupplot[
        xmin=0,xmax=200,
        xtick={0,100,200},
        axis y line*=right,
        restrict x to domain*=0:,
      ]
      \pgfplotsinvokeforeach{1,...,4}{%
        \addplot table [x index=#1,y index=0] {\mytable};%
      }
      \legend{Cat1,Cat2,Cat3,Cat4}
  \end{groupplot}
\end{tikzpicture}
\end{document}

or

enter image description here

Code:

\documentclass[border=5mm]{standalone}
\usepackage{pgfplots}
\usepackage{pgfplotstable}
\usepgfplotslibrary{groupplots}
\pgfplotsset{compat=1.10}

\pgfplotstableread{
Year    Cat1  Cat2    Cat3   Cat4
2005    10     50      -10     30  
2006    -40     60      -15     90
2007    -20     60      -15     60
}\mytable

\begin{document}%
\begin{tikzpicture}
  \begin{groupplot}[
      group style={
        group size = 2 by 1,
        horizontal sep=0pt,
        yticklabels at=edge left,
        },
      xbar stacked,
      ytick=data,
      yticklabel style={/pgf/number format/1000 sep=},
      enlarge y limits=0.25,
      scale only axis,
      height=5.7cm
    ]
    \nextgroupplot[
        xmin=-100,xmax=0,width=2.5cm,
        xtick={-100,-50},
        axis y line*=left,
        restrict x to domain*=:0,
      ]
      \pgfplotsinvokeforeach{1,...,4}{%
        \addplot table [x index=#1,y index=0] {\mytable};%
      }
    \nextgroupplot[
        xmin=0,xmax=200,width=5cm,
        xtick={0,50,...,200},
        axis y line*=right,
        restrict x to domain*=0:,
      ]
      \pgfplotsinvokeforeach{1,...,4}{%
        \addplot table [x index=#1,y index=0] {\mytable};%
      }
      \legend{Cat1,Cat2,Cat3,Cat4}
  \end{groupplot}
\end{tikzpicture}
\end{document}
0

There is another workaround to the described problem. By adding disabledatascaling to the list of axis parameters, the weird behavior is gone. Unfortunately, this does not work in case of numbers larger than +/- 16,000 and in case of very small floating point numbers, but for many other cases it works. Here is the resulting source code:

\documentclass[border=5mm]{standalone}

\usepackage{pgfplots}
\usepackage{pgfplotstable}


\pgfplotstableread{
Year    Cat1  Cat2    Cat3   Cat4
2005    10.0     50.0      -40.0     30.0
2006    -40.0     60.0      -40.0     90.0
2007    60.0     -40.0      -40.0     60.0
}\mytable

\begin{document}%

  % Fourth try: explicitly defining the x-axis range to center the zero point, separate axis for positive and negative values
  \begin{tikzpicture}

    \begin{axis}[
      xbar stacked,
      restrict x to domain*=0:,
      xmin=-200, xmax=200,
      disabledatascaling,  % <-- this is important
      ]
      \addplot table [x index=1,y expr=\coordindex] {\mytable};
      \addplot table [x index=2,y expr=\coordindex] {\mytable};
      \addplot table [x index=3,y expr=\coordindex] {\mytable};
      \addplot table [x index=4,y expr=\coordindex] {\mytable};
      \legend{Cat1,Cat2,Cat3,Cat4}

    \end{axis}

    \begin{axis}[
      xbar stacked,
      restrict x to domain*=:0,
      xmin=-200, xmax=200,
      xtick=\empty,
      ytick=\empty,
      axis x line=none,
      axis y line=none,
      disabledatascaling,  % <-- this is important
      ]
      \addplot table [x index=1,y expr=\coordindex] {\mytable};
      \addplot table [x index=2,y expr=\coordindex] {\mytable};
      \addplot table [x index=3,y expr=\coordindex] {\mytable};
      \addplot table [x index=4,y expr=\coordindex] {\mytable};
      \legend{Cat1,Cat2,Cat3,Cat4}

    \end{axis}
  \end{tikzpicture}

\end{document}

Using this, the following graph is produced: Resulting graph using the described workaround

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .