block by michalskop 9633867

Pie chart vs. Bar chart fight

Full Screen

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>

g {
  font: 10px sans-serif;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.x.axis path {
  display: none;
}

.line {
  fill: none;
  stroke: #bbb;
  stroke-width: 1px;
  opacity: .5;
}
.line.active {
  stroke-width: 5px;
  stroke: #444
}
.group-2 {
  fill: none;
  stroke: #bb0;
  stroke-width: 1px;
}
.group-2.active {
  stroke-width: 5px;
  stroke: #880;
}

.zeroline {
  fill: none;
  stroke: red;
  stroke-width: 3px;
}

.pie {
  fill: green;
  stroke: #222;
  opacity: .5;
}
.pie.active {
  fill: #222;
  opacity: 1;
}

.bar {
  fill: blue;
  stroke: #222;
  opacity: .5;
}
.bar.active {
  fill: #222;
  opacity: 1;
}

.vertical {
  stroke-width: 3px;
  opacity: 1;
}
.vertical.bar {
  stroke: blue;
}
.vertical.pie {
  stroke: green;
}
.vertical.active {
  stroke-width: 5px;
}
.group {
  font-size: 2em;
}
.g1 {
  border:1px solid #bbb;
}
.g2 {
  border:1px solid #bb0;
}
.e.pie {
  border:3px solid green;
  padding: 2px;
  opacity: 1;
}
.e.bar {
  border:3px solid blue;
  padding: 2px;
  opacity: 1;
}

p {
  font: 1em sans-serif;
}

</style>

<body>
<h1>Pie chart vs. Bar chart figth</h1>

<div id="chart"></div>
<p>
Experiment conducted on 22 university students divided into 2 groups (11 each). Each group was shown 1 pie chart and 1 bar chart showing election results. The students were asked to estimate the percentage won by each party (later rescaled to sum 100%).
</p>
<p>
The chart above summarizes the experiment's results:
<ul>
  <li>Every point is one error of estimate (by one student)</li>
  <li>Green circles are error of estimates based on a pie chart</li>
  <li>Blue squares are error of estimates based on a bar chart</li>
  <li>Horizontal lines connect estimates by one person, colored by the election</li>
  <li>Vertical lines show variance, the 2 extreme error of estimates in each groups are taken away</li>
  <li>Green lines are for error of estimates from pie chart, blue lines are for bar chart</li>
  <li>The values on x-axis are slightly randomly moved, so the overlapped points may be visible.</li>
</ul>
</p>
<p>
There is no clear evidence that any of the charts performs better, the results are slightly better for pie charts. But the sample is not big enough for better conclusions.
</p>
<p>The actual file used for the experiment is <a href="https://docs.google.com/document/d/1YqkQzXvi79k_Uoe_MFKD5uWy2oLxvnQkKU7TWc5y4qo/pub">here</a>. The table below shows the experimental design:</p>

<table>
  <tr><th></th><th><span class="group g1">Election 1</span></th><th><span class="group g2">Election 2</span></th></th>
  <tr><td><span class="group">Group 1:</span></td><td class="e pie"><image src="volby1998_2.png" title="Election 1, pie" /></td><td  class="e bar"><image src="volby2002.png" title="Election 2, bar" /></td></tr>
  <tr><td><span class="group">Group 2:</span></td><td  class="e bar"><image src="volby1998.png" title="Election 1, bar" /></td><td class="e pie"><image src="volby2002_2.png" title="Election 2, pie" /></td></tr>
</table>
<p>The real values were:<br/>
Election 1: 0.2910, 0.2499, 0.1804, 0.1793, 0.0994<br/>
Election 2: 0.3024, 0.2450, 0.1853, 0.1429, 0.1244
</p>
<script src="//d3js.org/d3.v3.js"></script>
<script>
var realvalues = [
 [0.2910,0.2499,0.1804,0.1793,0.09935],
 [0.3024,0.2450,0.1853,0.1429,0.1244]
];

var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 960 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

var x = d3.scale.linear()
    .range([0, width])
    .domain([0.05,0.35]);
var y = d3.scale.linear()
    .range([height, 0])
    .domain([-0.15,0.2]);
    
var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");
var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

var svg = d3.select("#chart").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
svg.append("g")
  .attr("class", "x axis")
  .attr("transform", "translate(0," + height + ")")
  .call(xAxis)
 .append("text")
  .attr("x", x(.35))
  .attr("dx", ".71em")
  .style("text-anchor", "end")
  .text("Real values of percentage");
  
svg.append("g")
  .attr("class", "y axis")
  .call(yAxis)
 .append("text")
  .attr("transform", "rotate(-90)")
  .attr("y", 6)
  .attr("dy", ".71em")
  .style("text-anchor", "end")
  .text("Errors of Estimates");

var line = d3.svg.line()
    .x(
    function(d) { 
    return d[0]; })
    .y(function(d) { return d[1]; });
    
zeroline = Array([x(0.05),y(0)],[x(0.35),y(0)]);

var r = 3;

svg.append("path")
      .datum(zeroline)
      .attr("class", "zeroline")
      .attr("d", line);
      
d3.csv("data.csv", function(error, data) {
  points = [[],[]]; // 2 groups
  realvalues.forEach(function(rd,ri) {
    pdata = [[],[]]; // 2 chart types
    for (i in realvalues[ri]) {
      pdata[0][i] = [];
      pdata[1][i] = [];
    }
    data.forEach(function(d) {
      rdata = Array();
      for (i in realvalues[ri]) {
        xx = realvalues[ri][i] + Math.random()/200-0.0025;
        if (ri == 0) letter = "a";
        else letter = "b";
        k = parseInt(i) + 1;
        yy = +d[letter+k]-realvalues[ri][i];
        rdata.push([x(xx),y(yy)]);
        //type of chart:
        if (((d.group == "1") && (ri == 0)) || ((d.group == "2") && (ri == 1)))
          ct = 1;  //bar
        else
          ct = 0;  //pie
        points[ct].push([xx,yy]);
        pdata[ct][parseInt(i)].push(yy);
      }
      svg.append("path")
      .datum(rdata)
      .attr("class", function() {if (ri == 1) return "line group-2"; else return "line"})
      .attr("d", line)
      .attr("title",d.name)
      .on("mouseover", function() {
          d3.select(this)
            .classed("active", true ) // should then accept fill from CSS
        })
      .on("mouseout",  function() {
          d3.select(this)
            .classed("active", false)
          });
    });
    
    //vertical lines
    for (ct in pdata) {
      for (i in pdata[ct]) {
        pdata[ct][i] = pdata[ct][i].sort(compareNumbers);
      }
    }
        //prepare ends (eliminate extremes)
    pends = [[],[]];  //2 charts    
    pmid = [[],[]];
    for (ct in pdata) {
      for (i in pdata[ct]) {
        pends[ct][i] = [];
        pends[ct][i].push([x(realvalues[ri][parseInt(i)])+3*parseInt(ct),y(pdata[ct][i][1])]);
        pends[ct][i].push([x(realvalues[ri][parseInt(i)])+3*parseInt(ct),y(pdata[ct][i][pdata[ct][i].length-2])]);
        pmid[ct][i] = [realvalues[ri], pdata[ct][i][Math.round(pdata[ct][i].length/2-1)]]; //approx, ok for odd numbers
        
        svg.append("path")
          .datum(pends[ct][i])
          .attr("class", function() {if (ct == "1") return "vertical bar"; else return "vertical pie"})
          .attr("d", line)
          //.attr("title",d.name)
          .on("mouseover", function() {
              d3.select(this)
                .classed("active", true ) // should then accept fill from CSS
            })
          .on("mouseout",  function() {
              d3.select(this)
                .classed("active", false)
              });
      }
    }
    
    
    
    
  });
  //points
  svg.selectAll('circle')
    .data(points[0])
    .enter().append('circle')
    .attr("cx", function(d) {return x(d[0]) })
    .attr("cy", function(d) {return y(d[1]) })
    .attr("r", 3)
    .attr("class", "pie")
    .attr("title",function(d) {return "Error of colour estimate: " + Math.round(d[1]*1000)/10 + "% (percentage points)"})
    .on("mouseover", function() {
          d3.select(this)
            .classed("active", true )
        })
    .on("mouseout",  function() {
          d3.select(this)
            .classed("active", false)
     }); 
  svg.selectAll('rect')
    .data(points[1])
    .enter().append('rect')
    .attr("x", function(d) {return x(d[0]) - r })
    .attr("y", function(d) {return y(d[1]) - r})
    .attr("height", 2*r)
    .attr("width", 2*r)
    .attr("class", "bar")
    .attr("title",function(d) {return "Error of colour estimate: " + Math.round(d[1]*1000)/10 + "% (percentage points)"})
    .on("mouseover", function() {
          d3.select(this)
            .classed("active", true )
        })
    .on("mouseout",  function() {
          d3.select(this)
            .classed("active", false)
     }); 
});

function compareNumbers(a, b) {
  return a - b;
}

</script>

    <script>
      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
      })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

      ga('create', 'UA-8592359-13', 'ocks.org');
      ga('send', 'pageview');

    </script>

</body>

data.csv

name,group,a1,a2,a3,a4,a5,b1,b2,b3,b4,b5
Person 1,1,0.380952381,0.2857142857,0.1428571429,0.1428571429,0.0476190476,0.35,0.25,0.15,0.2,0.05
Person 2,1,0.3,0.25,0.2,0.2,0.05,0.4,0.3,0.15,0.1,0.05
Person 3,1,0.31,0.23,0.2,0.2,0.06,0.3,0.25,0.18,0.15,0.12
Person 4,1,0.35,0.25,0.15,0.15,0.1,0.3,0.25,0.2,0.15,0.1
Person 5,1,0.4,0.35,0.1,0.1,0.05,0.37,0.25,0.15,0.13,0.1
Person 6,1,0.25,0.2391304348,0.2065217391,0.2065217391,0.097826087,0.2696629213,0.2471910112,0.1685393258,0.1685393258,0.1460674157
Person 7,1,0.375,0.3125,0.125,0.125,0.0625,0.3157894737,0.2631578947,0.1578947368,0.1578947368,0.1052631579
Person 8,1,0.3157894737,0.2631578947,0.1578947368,0.1578947368,0.1052631579,0.2772277228,0.2376237624,0.1782178218,0.1683168317,0.1386138614
Person 9,1,0.4,0.3,0.12,0.12,0.06,0.35,0.25,0.2,0.15,0.05
Person 10,1,0.3076923077,0.2435897436,0.1666666667,0.1666666667,0.1153846154,0.2947368421,0.2736842105,0.1894736842,0.1473684211,0.0947368421
Person 11,1,0.27,0.23,0.2,0.2,0.1,0.29,0.26,0.18,0.15,0.12
Person 12,2,0.2947368421,0.2315789474,0.1789473684,0.2105263158,0.0842105263,0.3,0.25,0.2,0.15,0.1
Person 13,2,0.33,0.22,0.18,0.16,0.11,0.3203883495,0.2330097087,0.1747572816,0.145631068,0.1262135922
Person 14,2,0.2678571429,0.25,0.1696428571,0.1607142857,0.1517857143,0.2857142857,0.2244897959,0.2040816327,0.1530612245,0.1326530612
Person 15,2,0.3296703297,0.3076923077,0.1318681319,0.1648351648,0.0659340659,0.3,0.25,0.2,0.15,0.1
Person 16,2,0.2545454545,0.2363636364,0.1909090909,0.1818181818,0.1363636364,0.3225806452,0.2688172043,0.2150537634,0.1075268817,0.0860215054
Person 17,2,0.35,0.25,0.18,0.1,0.12,0.36,0.3,0.2,0.1,0.04
Person 18,2,0.2967032967,0.2637362637,0.1538461538,0.1758241758,0.1098901099,0.2777777778,0.2333333333,0.1888888889,0.1666666667,0.1333333333
Person 19,2,0.3614457831,0.2048192771,0.1445783133,0.1686746988,0.1204819277,0.2830188679,0.2264150943,0.1886792453,0.1603773585,0.141509434
Person 20,2,0.3333333333,0.2222222222,0.1666666667,0.1666666667,0.1111111111,0.3684210526,0.2631578947,0.2105263158,0.1052631579,0.0526315789
Person 21,2,0.2830188679,0.2358490566,0.1886792453,0.2169811321,0.0754716981,0.4,0.28,0.16,0.1,0.06
Person 22,2,0.3539823009,0.2212389381,0.1769911504,0.1592920354,0.0884955752,0.4838709677,0.3225806452,0.1075268817,0.0537634409,0.0322580645