Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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}}}}}
share|improve this question
    
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. –  rm -rf Oct 16 '12 at 21:45
    
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. –  MLD Oct 16 '12 at 22:12
    
Well, only a minimal example was needed... so 2 bars and 2 clusters should be sufficient. –  rm -rf Oct 16 '12 at 22:17
    
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? –  Mike Honeychurch Oct 16 '12 at 22:56
1  
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) –  Mike Honeychurch Oct 16 '12 at 23:14

3 Answers 3

up vote 11 down vote accepted

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.

share|improve this answer
    
Thanks a lot! For the chart and the references/suggestions for moving forward. –  MLD Oct 17 '12 at 22:05

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]
share|improve this answer

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

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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