Clustered and Grouped Bar Chart in Mathematica v8.0
Unfortunately I was not able to come back to this problem during the day. This is a bit of a hack created after some red wine induced insomnia but here goes:
First a labelling function:
ClearAll[labeler];
labeler[v_, {i_, j_}, __] := (Which[
j == 1, Clear[tmp]; tmp = v,
j == 2, tmp = tmp + v
]; Placed[Row[{tmp}], Center])
Then the code for the bar chart (note that I had to add {0,0} as a spacer. This seems an unreasonable approach to take but was quick to achieve the desired result):
BarChart[{{92, 8}, {92, 8}, {77, 23}, {100, 0}, {0, 0}, {78, 21}, {78,
16}, {82, 14}, {83, 16}, {0, 0}, {68, 24}, {72, 23}, {78,
20}, {75, 20}, {0, 0}, {56, 36}, {59, 31}, {66, 28}, {59, 35}, {0,
0}, {84, 7}, {67, 23}, {85, 9}, {80, 14}},
AspectRatio -> 0.4,
BaseStyle -> Directive[FontFamily -> "Helvetica", 10, GrayLevel[0.6]],
BarSpacing -> 0.2,
ChartBaseStyle -> Directive[EdgeForm[]],
ChartLabels -> {Flatten[
Table[{"Bar\[VeryThinSpace]1", "Bar\[VeryThinSpace]2", "Bar3",
"Bar\[VeryThinSpace]4", ""}, {5}]], None},
ChartLayout -> "Stacked",
ChartStyle -> {RGBColor[0.6, 0.8, 0.196], RGBColor[1., 0.743, 0]},
Frame -> {{True, False}, {True, True}},
FrameLabel -> {None,
Style["Utilization/Availability (% of Calendar Time)",
GrayLevel[0.2]], None, None},
FrameStyle -> GrayLevel[0.7],
FrameTicks -> {{{2.5, "\nCluster1", 0}, {7.5, "\nCluster2",
0}, {12.5, "\nCluster3", 0}, {17.5, "\nCluster4", 0}, {22.5,
"\nCluster4", 0}}, Automatic, None, None},
FrameTicksStyle -> GrayLevel[0.2],
GridLines -> {None, Automatic},
GridLinesStyle -> GrayLevel[0.7],
ImageSize -> 800,
LabelingFunction -> labeler,
PerformanceGoal -> "Speed",
PlotRangePadding -> {{0.3, 0.3}, {0, 0}},
Ticks -> {None, Automatic}]
Below is a screen shot:
and your plot:
I haven't added the legend but you'll find plenty of examples of good legend code here (@Jens being the best and most often cited) -- main thing is avoid the bundled PlotLegends
. Also if you want something identical to the Excel plot you would need to remove minor ticks on the y axis and have major ticks and grid lines every 10 units.
To get the lower labels with the horizontal bar (mimic the Excel plot) I would use Labeled
with the Grid
of the labels and a top Divider
line.
Following on from provided comments, the following code will format the labels (centering the "cluster" label), however it is only an interim solution (more work is required to determine how to format and manipulate the data)
BarChart[{{92, 8}, {92, 8}, {0, 0}, {78, 8}, {72, 7}},
ChartLayout -> "Stacked",
Ticks -> None,
PlotRangePadding -> {{0, 0}, {8, 0}},
Frame -> {{False, False}, {True, False}},
FrameStyle -> Directive[White, FontColor -> Black],
FrameTicks -> {{{1.5, "Cluster1", 0}, {4.5, "Cluster2", 0}}, None, None, None},
ChartLabels -> {{"bar1", "bar2", "", "bar1", "bar2"}, None},
ChartLegends -> {"Av", "Ut"}] /.
Column[List[a : Grid[List[___]] ..]] :> Column[Reverse@List@a]
I was asked how to do this recently and came up with an independent approach using Show
and RectangleChart
. It was tested in version 9 but should work in version 8. It automatically formats the data as required, and cycles the colours in the stack if needed, as long as the list of colours is longer than the number of bars in each stack.
groupstackBarChart[rawdata : {__?MatrixQ},
options : OptionsPattern[{groupstackBarChart, RectangleChart, Graphics}]] /;
Length@rawdata > 1 :=
Module[{n = Length[rawdata],
bs = With[{ovbs = OptionValue[BarSpacing]},
Which[ovbs === Automatic, 0.2, VectorQ[ovbs], First@ovbs,
NumericQ[ovbs], ovbs, True, 0.2]], cs, step1, transformeddata, zeros},
cs = With[{csraw = OptionValue[ChartStyle]},
Which[VectorQ[csraw],
Partition[PadRight[csraw, Total[Dimensions[#][[2]] & /@ rawdata], csraw],
n], MatrixQ[csraw] && Length[csraw] >= Length[rawdata], csraw,
True, NestList[Lighter /@ # &,
Darker /@ {Red, Blue, Green, Orange, Purple, Pink, Gray},
n - 1]]];
step1 = (Riffle[#, zeros] & /@ Map[{1, #} &, rawdata, {-1}]) /.
zeros -> Sequence @@ ConstantArray[{{1, 0}}, n - 1];
(* If you put the list directly in the Riffle,
it alternates between the elements, which is not what you want*)
transformeddata =
MapIndexed[RotateRight[#1, First[#2] - 1] &,
Join[#, ConstantArray[{{1 - bs, 0}}, n - 1]] & /@ step1];
Show[Sequence @@ (MapThread[
RectangleChart[#1, ChartLayout -> "Stacked", BarSpacing -> bs, ChartStyle -> #2,
Sequence @@ FilterRules[{options}, Options[RectangleChart]]] &,
{transformeddata, cs}]), Sequence @@ FilterRules[{options}, Options[Graphics]]
]]
Here are some examples
testdata = RandomVariate[TriangularDistribution[{-0.5, 2}], {3, 5, 3}];
testdata2 = RandomVariate[TriangularDistribution[{-0.5, 2}], {4, 4, 4}];
A simple example
groupstackBarChart[testdata]
Showing the colour cycling, though this can be confusing if you have negative values:
groupstackBarChart[testdata, ChartStyle -> {Orange, Gray, Cyan, Purple}]
Something closer to what was requested:
groupstackBarChart[testdata2,
ChartStyle ->
ConstantArray[{RGBColor[0.6, 0.8, 0.196], RGBColor[1., 0.743, 0]},
6], FrameTicks -> {Join[
Table[{i, "Cluster " <> ToString[Round[(i + 4)/7]], {0, 0}}, {i,
3, 25.5, 7.25}]], Range[0, 3, 0.5]}, Axes -> None,
Frame -> {{True, False}, {True, False}},
GridLines -> {Range[6.5, 28,
7.25], {{0, Directive[Black, Dashing[{}]]},
Sequence @@ Range[0.5, 3, 0.5]}},
GridLinesStyle -> Directive[Gray, Dashing[Small]]]