6
$\begingroup$

I have a dataset comparing the performance of students on two exams, Exam 1 and Exam 2: enter image description here

I would like to create a stacked bar chart where each bar represents the grades on Exam 1 and the stacks in each bar represent the grades on Exam 2. Here's the catch: instead of having solid bars representing the number of students, I want to have a symbol (eg, circle) represent each student. See drawing below: enter image description here

So by counting the number of dots in the A column, I know how many students got an A on Exam 1. By counting the total number of green dots, I know how any students got an A on Exam 2. By counting the number of green dots in column A, I know how many students got As on Exam 1 and Exam 2.

I am, of course, open to any suggestions on how to display data of this type that conveys the same information.

$\endgroup$

1 Answer 1

4
$\begingroup$

Update: Using a custom ChartElementFunction to represent each data point with a disk:

ClearAll[ceF]
ceF[nc_: 5, sz_: 5, shft_: 5][ clr_: "Pastel"] := 
  Module[{nrows, disks, bb = Charting`ChartStyleInformation["BoundingBox"][[2]], 
     ncols = Range[#[[1, 1]], #[[1, 2]], (#[[1, 2]] - #[[1, 1]])/(nc - 1)], 
     cols = If[Head[clr] === String, 
      ColorData[clr] /@ Range[0, 1, 1/(Length[#3[[1]]] - 1)], clr]}, 
    nrows = Range[## & @@ (shft+bb), (bb[[2]] - bb[[1]])/Quotient[bb[[2]] - bb[[1]], nc]];
    disks = Disk[#, Offset[sz]] & /@ (Reverse /@ Tuples[{nrows, ncols}][[;; #2]]); 
    Thread[{Join @@ ConstantArray @@@ Thread[{cols, #3[[1]]}], disks}]] &;

Examples:

Use the total of each data group as main data and pass its breakdown as metadata to be processed by the ChartElementFunction to render bars filled with appropriate number of colored disks:

data = {{10, 8, 2}, {12, 15, 4}, {5, 7, 7}};
bcdata = Thread[Total /@ data -> data];

Labeled[BarChart[bcdata, ChartElementFunction -> ceF[][], 
  BarSpacing -> {1, 0}, ChartLabels -> {Style[#, 16] & /@ {"A", "B", "<=C"}}, 
  Axes -> {True, False}, 
  ChartLegends -> SwatchLegend["Pastel", Style[#, 16] & /@ {"A", "B", "<=C"}, 
    LegendLayout -> "ReversedColumn", 
    LegendLabel -> Style["Exam 2 Grades", 16, "Panel"]]], 
 {Rotate[Style["Counts", 16, "Panel"], 90 Degree], 
  Style["Exam 1", 16, "Panel"]}, {Left, Bottom}]

enter image description here

Use ChartElementFunction -> ceF[][{Red, Green, Blue}] with appropriate changes in the legend to get

enter image description here

Use ChartElementFunction -> ceF[3, 7][{Red, Green, Blue}] to get larger disks arranged in three columns:

enter image description here

etc..

Original answer:

data = {{10, 8, 2}, {12, 15, 4}, {5, 7, 7}};

Using a standard BarChart you can do

Labeled[BarChart[data, ChartLayout -> "Stacked", 
   ChartLabels -> {Style[#, 16] & /@ {"A", "B", "<=C"}, None},
   ChartLegends -> SwatchLegend[Style[#, 16] & /@ {"A", "B", "<=C"}, 
     LegendLayout -> "ReversedColumn", 
     LegendLabel -> Style["Exam 2 \nGrades", 16, "Panel"]]], 
 {Rotate[Style["Count", 16, "Panel"], 90 Degree], Style["Exam 1", 16, "Panel"]}, 
 {Left, Bottom}]

enter image description here

Use labeleddata

labeleddata = Thread[Labeled[#, StringJoin @@@ Thread[{{"A : ", "B : ", "<=C : "}, 
  ToString /@ #}], Center]] & /@ data;

as the first argument of BarChart above to get

enter image description here

$\endgroup$

Your Answer

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

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