[Tex/LaTex] Two boxplots per plot entry – shifting boxplots in pgfplots

boxplotpgfplotstikz-pgf

I'm trying to compare the results of a control group and an experimental group under different conditions. To simplify, let's assume there is only one condition (ie. only one mark along the x axis using boxplot/draw direction=y) and I'd like there to be two boxplots on top of this, one on each side of the mark.

Here's an example of a similar plot to the one I'm trying to make now made in the past (this one has five conditions, but the added complexity I guess illustrates the benefits of such a layout):

Comparing boxplots per condition

In the past (ie. pre pgfplots <1.8) I used to accomplish this by using a pair of custom styles: rshift and lshift, which would shift each plot a given amount to either the right or the left:

\pgfplotsset{
  rshift/.style={
    xshift=\pgfkeysvalueof{/pgfplots/rshift scale},
    legend image post style={xshift=-\pgfkeysvalueof{/pgfplots/rshift scale}}
  },
  lshift/.style={
    xshift=-\pgfkeysvalueof{/pgfplots/lshift scale},
    legend image post style={xshift=\pgfkeysvalueof{/pgfplots/lshift scale}}
  },
  rshift scale/.initial=0.2em,
  lshift scale/.initial=0.2em,
}

However, the new boxplots code overrides these shifts, breaking my hack. So, now that we have a (greatly and undeniably) improved interface for boxplots, what would be the way to do a plot like the one shown (of course, ignoring the syling), without having to revert to the old way of doing things?

The boxplot style

The answer by Christian Feuersänger implemented his own version of the style in the picture, so I thought I might provide the one I'm using now. This is only the style for the boxplots, though, not margins and ticks.

\pgfplotsset{
  % Fixed box width
  boxplot/box width/.initial=1em,
  % Solid boxes with fixed box width
  solid boxes/.style={
    mark=x,
    boxplot/draw direction=y,
    boxplot/whisker extend=0,
    boxplot/draw/median/.code={%
      \draw[mark size=2pt,/pgfplots/boxplot/every median/.try]
        \pgfextra
        \pgftransformshift{
          \pgfplotsboxplotpointabbox
            {\pgfplotsboxplotvalue{median}}
            {0.5}
        }
        \pgfsetfillcolor{white}
        \pgfuseplotmark{*}
        \endpgfextra
      ;
    },
    boxplot/draw/box/.code={
      \draw[fill,/pgfplots/boxplot/every box/.try]
        ($(boxplot box cs:\pgfplotsboxplotvalue{lower quartile},0.5)!0.5\pgfkeysvalueof{/pgfplots/boxplot/box width}!(boxplot box cs:\pgfplotsboxplotvalue{lower quartile},0)$)
        rectangle
        ($(boxplot box cs:\pgfplotsboxplotvalue{upper quartile},0.5)!0.5\pgfkeysvalueof{/pgfplots/boxplot/box width}!(boxplot box cs:\pgfplotsboxplotvalue{upper quartile},1)$)
      ;
    }
  },
}

Best Answer

In principle, you can just provide one or more box plots here. In your case, it appears to be boxplot prepared with suitable arguments.

I rarely had the opportunity to study common box plot styles, so I took the freedom to adopt pgfplots to your style. Maybe I can add such styles to pgfplots eventually, so feedback of sorts "I miss a style like XYZ" are welcome.

Anyway, I arrived at the following which appears to be quite close to what you want to archieve:

enter image description here

\documentclass{standalone}

\usepackage{pgfplots}
\usepgfplotslibrary{statistics}

\pgfplotsset{
    jja style/.style={
        % draw whiskers as a single line:
        boxplot/draw/whisker/.code 2 args={%
            \draw[/pgfplots/boxplot/every whisker/.try]
                (boxplot cs:##1) -- (boxplot cs:##2)
            ;
        },%
        %
        % fill the boxes:
        boxplot/every box/.style={
            fill,
        },
        % 
        % the median should be visualized as a circle:
        boxplot/draw/median/.code={%
            \draw[fill=white]
                (boxplot cs:\pgfplotsboxplotvalue{median}) circle (3pt)
            ;
        },
        %
        % do not clip to avoid problems with the median:
        clip=false,
        %
        boxplot/draw direction=y,
        %
        % configure axis lines:
        x axis line style={opacity=0},
        axis x line*=bottom,
        axis y line=left,
        %
        ymajorgrids,
        yminorgrids,
        minor y tick num=1,
        ytick={0,50,100},
        ylabel=\%,
        ylabel style={rotate=-90},
        %
        % width of boxes:
        boxplot/box extend=0.3,
    },
    %
    %
    rshift/.style={
        xshift=\pgfkeysvalueof{/pgfplots/rshift scale},
        legend image post style={xshift=-\pgfkeysvalueof{/pgfplots/rshift scale}},
        blue!70!black,
    },
    lshift/.style={
        xshift=-\pgfkeysvalueof{/pgfplots/lshift scale},
        legend image post style={xshift=\pgfkeysvalueof{/pgfplots/lshift scale}},
        orange,
    },
    rshift scale/.initial=1em,
    lshift scale/.initial=1em,
}
\begin{document}

\begin{tikzpicture}
\begin{axis}[
    jja style,
    ymin=0,
    ymax=100,
    xtick={1,2,3},
    xticklabels={isol,qfin,qint},
]
\addplot+[
    forget plot,
    rshift,
    boxplot prepared={
        lower whisker=27, lower quartile=45,
        median=59,
        upper quartile=61, upper whisker=70,
    },
]
    coordinates {};

\addplot+[
    lshift,
    boxplot prepared={
        lower whisker=100, lower quartile=100,
        median=100,
        upper quartile=100, upper whisker=100,
    },
]
    coordinates {};


\addplot+[
    forget plot,
    rshift,
    boxplot prepared={
        lower whisker=47, lower quartile=50,
        median=57,
        upper quartile=70, upper whisker=71,
    },
]
    coordinates {};
\addplot+[
    lshift,
    boxplot prepared={
        lower whisker=87, lower quartile=92,
        median=100,
        upper quartile=100, upper whisker=100,
    },
]
    coordinates {};
\end{axis}
\end{tikzpicture}

\end{document}

(my convert outputs used to be better; no idea why the quality of the screenshot looks so degenerate!?)

I do not know what you mean by "breaking my hack", but I would be interested in why and how pgfplots "broke" something. Breaking compatibility is surely something that it should not do, so I consider any problem of this sorts a bug report. I would appreciate it if you would send an email to me which illustrates the problem (some minimal working example).