Since the BoxWhiskerChart
doesn't label its x-axis coordinate, I decided to make use of the functionality that already exists in ErrorListPlot
. The latter is part of the "ErrorBarPlots"
package which has to be loaded explicitly.
First I generate the data, then I extract a list of coordinates for ErrorListPlot
, then I specify a function that renders the "error bar" as a BoxWhiskerChart
for each data set individually. The scale has to be common to all plots, and that's achieved with a combination of fixed PlotRange
and AspectRatio -> Full
for the inset whisker plots.
data = {{1, RandomInteger[{-10, 10}, 10]}, {3,
RandomInteger[{-10, 10}, 10]}, {10, RandomInteger[{-10, 10}, 10]}};
data = {{1, {7, -9, 5, 1, 3, 8, 10, 10, -3,
0}}, {3, {-8, -6, -6, 7, -8, -8, 2, -8, -2, -4}}, {10, {-9, -3,
9, 5, -1, 0, 9, 2, 3, -1}}};
dataValues = data[[All, 2]];
The original chart is here:
BoxWhiskerChart[dataValues]
Now for the modifications to ErrorListPlot
:
{min, max} = {Min[#], Max[#]} &@Flatten[dataValues]
(* ==> {-9, 10} *)
kValues = MapIndexed[Flatten[{#, min, #2}] &, data[[All, 1]]];
whiskers = Map[
BoxWhiskerChart[#, Frame -> None, PlotRange -> {min, max},
AspectRatio -> Full, PlotRangePadding -> 0,
PlotRangeClipping -> False] &,
dataValues];
Needs["ErrorBarPlots`"]
errorfunction[{x_, y_}, err_] := Inset[
whiskers[[Last[Flatten[List @@ err]]]],
{x, y}, {Center, Bottom}, Scaled[{.1, 1}]]
ErrorListPlot[kValues, ErrorBarFunction -> errorfunction,
PlotMarkers -> "", PlotRange -> {min, max}]
The tooltips of the original chart still work in this plot, too.
Edit
In response to the additional question about logarithmic x axis, we can work with the same data but can't use ErrorListPlot
. So instead one could do this:
ListLogLinearPlot[
List /@ kValues[[All, 1 ;; 2]],
PlotMarkers -> Map[{#, 1} &, whiskers],
PlotRange -> {min, max}] /.
Inset[i_, pos_, align_, scl_] :>
Inset[i, pos, {Center, Bottom}, Scaled[{.1, 1}]]
The idea is the same as above: "inject" the BoxWhiskerChart
at the correct location by means of an Inset
. The positioning can be taken care of by the plot function, but the scaling is not under my control in the plotting process. So I have to fix that by post-processing using the replacement /.
with a rule that looks for Inset
in the finished plot and aligns its bottom with the plot point coordinate. The latter is always equal to the minimum data value (min
), so that by setting PlotRange
and using Scaled[{.1, 1}]
for the fourth Inset
argument we get the same result as in ErrorListPlot
, only with a logarithmic axis:
This last approach would of course also work with ListPlot
and could therefore be a replacement for my my first solution.
A limitation of both approaches is that the vertical PlotRange
must correspond exactly to the one in the BoxWhiskerChart
, so that the scaling works properly by assuming the Inset
should span 100%
of the vertical space. This means you can't add PlotRangePadding
in the vertical direction. You can add it in the horizontal direction, though. Here is an illustration of an explicit horizontal PlotRange
to keep the bars away from the edges:
ListLogLinearPlot[
List /@ kValues[[All, 1 ;; 2]],
PlotMarkers -> Map[{#, 1} &, whiskers],
AxesOrigin -> {.9, 0},
PlotRange -> {{.9, 11}, {min, max}}] /.
Inset[i_, pos_, align_, scl_] :>
Inset[i, pos, {Center, Bottom}, Scaled[{.1, 1}]]