17
$\begingroup$

How would I create a clustered and grouped bar chart that looks like this (which was painstakingly created using Excel)? enter image description here

I have seen examples for Mathematica v6.0 but none for Mathematica v8.0.

The data I used to create this chart is as follows:

"Cluster","KPI","Bar","Value"
"Cluster1","Utilisation","Bar1",92
"Cluster2","Utilisation","Bar1",78
"Cluster3","Utilisation","Bar1",68
"Cluster4","Utilisation","Bar1",56
"Cluster5","Utilisation","Bar1",84
"Cluster1","Availability","Bar1",8
"Cluster2","Availability","Bar1",21
"Cluster3","Availability","Bar1",24
"Cluster4","Availability","Bar1",36
"Cluster5","Availability","Bar1",7
"Cluster1","Utilisation","Bar2",92
"Cluster2","Utilisation","Bar2",78
"Cluster3","Utilisation","Bar2",72
"Cluster4","Utilisation","Bar2",59
"Cluster5","Utilisation","Bar2",67
"Cluster1","Availability","Bar2",8
"Cluster2","Availability","Bar2",16
"Cluster3","Availability","Bar2",23
"Cluster4","Availability","Bar2",31
"Cluster5","Availability","Bar2",23
"Cluster1","Utilisation","Bar3",77
"Cluster2","Utilisation","Bar3",82
"Cluster3","Utilisation","Bar3",78
"Cluster4","Utilisation","Bar3",66
"Cluster5","Utilisation","Bar3",85
"Cluster1","Availability","Bar3",23
"Cluster2","Availability","Bar3",14
"Cluster3","Availability","Bar3",20
"Cluster4","Availability","Bar3",28
"Cluster5","Availability","Bar3",9
"Cluster1","Utilisation","Bar4",100
"Cluster2","Utilisation","Bar4",83
"Cluster3","Utilisation","Bar4",75
"Cluster4","Utilisation","Bar4",59
"Cluster5","Utilisation","Bar4",80
"Cluster1","Availability","Bar4",0
"Cluster2","Availability","Bar4",16
"Cluster3","Availability","Bar4",20
"Cluster4","Availability","Bar4",35
"Cluster5","Availability","Bar4",14

For convenience, here is the same sample data in a different form:

{{"Cluster1", {{92, 8}, {92, 8}, {77, 23}, {100, 0}}},
 {"Cluster2", {{78, 21}, {78, 16}, {82, 14}, {83, 16}}},
 {"Cluster3", {{68, 24}, {72, 23}, {78, 20}, {75, 20}}},
 {"Cluster4", {{56, 36}, {59, 31}, {66, 28}, {59, 35}}},
 {"Cluster5", {{84, 7}, {67, 23}, {85, 9}, {80, 14}}}}

Or:

{{"Cluster1",{{"Bar1",{92,8}},{"Bar2",{92,8}},{"Bar3",{77,23}},{"Bar4",{100,0}}}},
{"Cluster2",{{"Bar1",{78,21}},{"Bar2",{78,16}},{"Bar3",{82,14}},{"Bar4",{83,16}}}},
{"Cluster3",{{"Bar1",{68,24}},{"Bar2",{72,23}},{"Bar3",{78,20}},{"Bar4",{75,20}}}},
{"Cluster4",{{"Bar1",{56,36}},{"Bar2",{59,31}},{"Bar3",{66,28}},{"Bar4",{59,35}}}},
{"Cluster5",{{"Bar1",{84,7}},{"Bar2",{67,23}},{"Bar3",{85,9}},{"Bar4",{80,14}}}}}
$\endgroup$
9
  • $\begingroup$ Can you generate some fake sample data? If it were just a single cluster you could use the ChartLayout -> "Percentile" option in BarChart, but for multiple clusters, you might have to roll your own. $\endgroup$
    – rm -rf
    Commented Oct 16, 2012 at 21:45
  • $\begingroup$ I wasn't of the best way to lay out the data (which is why I originally didn't). However I have placed the data in flat format above in the question. $\endgroup$
    – MLD
    Commented Oct 16, 2012 at 22:12
  • $\begingroup$ Well, only a minimal example was needed... so 2 bars and 2 clusters should be sufficient. $\endgroup$
    – rm -rf
    Commented Oct 16, 2012 at 22:17
  • $\begingroup$ before spending some time on this can you clarify where you have said you have seen examples for Mma 6.0 but not 8.0. Does this mean you tried a V6.0 example in V8.0 and had problems? $\endgroup$ Commented Oct 16, 2012 at 22:56
  • 1
    $\begingroup$ when you evaluate Needs["BarCharts"]` you will get a message with a link to a page that shows how to transform the code to V8. The link showed me that your example would be written as BarChart[{{2, 2, 0, 4, 5}, {0.5, 0.5, 0, 0.5, 0.5}} // Transpose, ChartLayout -> "Stacked", ChartStyle -> {Yellow, Black}, Ticks -> {{{1.5, "1, 2"}, {4.5, "3, 4"}}, Automatic}]. Hopefully that might help. (You may need to check out the ChartLabels examples in the BarChart documentation.). If no answer is posted I'll have a go at it this afternoon (3-4 hours from now) $\endgroup$ Commented Oct 16, 2012 at 23:14

4 Answers 4

15
$\begingroup$

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:

enter image description here

and your plot:

enter image description here

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.

$\endgroup$
1
  • $\begingroup$ Thanks a lot! For the chart and the references/suggestions for moving forward. $\endgroup$
    – MLD
    Commented Oct 17, 2012 at 22:05
3
$\begingroup$

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]
$\endgroup$
3
$\begingroup$

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]

enter image description here

Showing the colour cycling, though this can be confusing if you have negative values:

groupstackBarChart[testdata, ChartStyle -> {Orange, Gray, Cyan, Purple}]

enter image description here

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]]]

enter image description here

$\endgroup$
0
$\begingroup$

You can do this without using FrameTicks and directly specifying values by using ChartLabels:

errors = {
    { "Molecule", "Delta H", "Relative Time" },
    {"HF/6-31G(d)", 64.6011, 1. },
    {"B3LYP/6-31G(d)", 69.384, 1.4413 },
    {"MP2/6-31G(d)", 19.2196, 1.38462 },
    {"MP4/6-31G(d)", 24.462, 7.76113 }
}

BarChart[ 
    errors[[ 2 ;;, 2 ;; ]], 
    ChartLegends -> errors[[ 1, 2 ;; ]], 
    ChartLabels -> { Placed[ prettyData[[ 2 ;; 5, 1 ]], Axis ], Placed[ { "", "", "", "" }, Axis ] }
]

It should be noted that in this case two were required inside the table for ChartLabels

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.