block by pstuffa b294fbd9a3a180499165

Anscombe Quartet

Full Screen

A D3 rendering of Anscombe Quartet, a classic data visualization example where four data sets have the same summary statistics, showing a reason why visualization is important - to see different distrubitions of data, which summary statistics can’t always uncover.

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<html>
<style type="text/css">

.axis path,
.axis line {

  fill: none;
  stroke: #000;
  stroke-opacity: .1;
  shape-rendering: crispEdges;

}

.dots {

  fill: #66ddff;
  fill-opacity: .75;
  stroke: steelblue;
  stroke-width: 2px;

}

body {

  font: 12px sans-serif;

}

.dots:hover{

  fill: red;
  stroke: #99212c;

}

.line {

  stroke: blue;
  fill:none;
  stroke-width: 3;

}


</style>

<body>

  <h2></h2>

  <button id="one">I</button>
  <button id="two">II</button>
  <button id="three">III</button>
  <button id="four">IV</button>

  <div class="g-chart"></div>

<script src="linearRegression.js" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js" charset="utf-8"></script>

<script>

  var margin = { top: 20, right: 20, bottom: 30, left: 40},
      width = 350,
      height = 350;

  var x = d3.scale.linear()
      .domain([0,20])
      .range([0, width]);

  var y = d3.scale.linear()
      .domain([0,20])
      .range([height, 0]);

  var xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom")
      .innerTickSize(-height)
      .outerTickSize(0)
      .tickPadding(10);

  var yAxis = d3.svg.axis()
      .scale(y)
      .orient("left")
      .innerTickSize(-width)
      .outerTickSize(0)
      .tickPadding(10);

  var svg = d3.select("body")
              .append("svg")
              .attr("height", height + margin.top + margin.bottom)
              .attr("width", width + margin.left + margin.right)
              .append("g")
              .attr("transform","translate(" + margin.left + "," + margin.top + ")");

  svg.append("rect")
    .attr("width",width)
    .attr("height", height)
    .style("fill-opacity", .05);

  svg.append("g")
    .attr("class","x axis")
    .attr("transform","translate(0," + height + ")")
    .call(xAxis)

  svg.append("g")
    .attr("class","y axis")
    .call(yAxis)


  d3.tsv("data.tsv", function(error, data) {

    if (error) throw error;

    dataOne = data.filter(function(d) { return d.group == 'I'; })
    dataTwo = data.filter(function(d) { return d.group == 'II'; })
    dataThree = data.filter(function(d) { return d.group == 'III'; })
    dataFour = data.filter(function(d) { return d.group == 'IV'; })

    data.forEach(function(d) {

      d.x = +d.x;
      d.y = +d.y;

    });

    lr = linearRegression(data.map(function(d) { return d.y; }), data.map(function(d) { return d.x; }))

    d3.select(".summary")
      .html(function(d) { return "slope: " + lr.slope + "<br />" +
                                 " intercept: " + lr.intercept + "<br />" +
                                 " r2: " + lr.r2;})

    var max = d3.max(data, function (d) { return d.x; });
    var myLine = svg.append("line")
                .attr("x1", x(0))
                .attr("y1", y(lr.intercept))
                .attr("x2", x(max))
                .attr("y2", y( (max * lr.slope) + lr.intercept ))
                .style("stroke", "black");


    d3.select("body")
      .select("h2")
      .text("Group I");

    dots = svg.selectAll(".dots")
      .data(dataOne, function(d,i) { return i; });

    dots.enter()
     .append("circle")
      .attr("class","dots")
      .attr("cx", function(d) { return x(d.x); })
      .attr("cy", function(d) { return y(d.y); })
      .attr("r", 8);


    d3.selectAll("#one")
    .on("click", function() {

    lr = linearRegression(dataOne.map(function(d) { return d.y; }), dataOne.map(function(d) { return d.x; }))
    d3.select(".summary")
      .html(function(d) { return "slope: " + lr.slope + "<br />" +
                                 " intercept: " + lr.intercept + "<br />" +
                                 " r2: " + lr.r2;})

      d3.select("body")
        .select("h2")
        .text("Group I")

      dots.data(dataOne)
        .transition()
        .duration(2000)
        .ease("elastic")
        .delay(function(d,i) { return i * 100; })
        .attr("cx", function(d) { return x(d.x); })
        .attr("cy", function(d) { return y(d.y); });
    });

    d3.selectAll("#two")
    .on("click", function() {

    lr = linearRegression(dataTwo.map(function(d) { return d.y; }), dataTwo.map(function(d) { return d.x; }))
    d3.select(".summary")
      .html(function(d) { return "slope: " + lr.slope + "<br />" +
                                 " intercept: " + lr.intercept + "<br />" +
                                 " r2: " + lr.r2;})

      var max = d3.max(dataTwo, function (d) { return d.x; });
      var myLine = svg.append("line")
                  .attr("x1", x(0))
                  .attr("y1", y(lr.intercept))
                  .attr("x2", x(max))
                  .attr("y2", y( (max * lr.slope) + lr.intercept ))
                  .style("stroke", "black"); 


      d3.select("body")
        .select("h2")
        .text("Group II")

      dots.data(dataTwo)
        .transition()
        .duration(2000)
        .ease("elastic")
        .delay(function(d,i) { return i * 100; })
        .attr("cx", function(d) { return x(d.x); })
        .attr("cy", function(d) { return y(d.y); });
    });

    d3.selectAll("#three")
    .on("click", function() {

    lr = linearRegression(dataThree.map(function(d) { return d.y; }), dataThree.map(function(d) { return d.x; }))
    d3.select(".summary")
      .html(function(d) { return "slope: " + lr.slope + "<br />" +
                                 " intercept: " + lr.intercept + "<br />" +
                                 " r2: " + lr.r2;})

      var max = d3.max(dataThree, function (d) { return d.x; });
      var myLine = svg.append("line")
                  .attr("x1", x(0))
                  .attr("y1", y(lr.intercept))
                  .attr("x2", x(max))
                  .attr("y2", y( (max * lr.slope) + lr.intercept ))
                  .style("stroke", "black"); 

      d3.select("body")
        .select("h2")
        .text("Group III")

      dots.data(dataThree)
        .transition()
        .duration(2000)
        .ease("elastic")
        .delay(function(d,i) { return i * 100; })
        .attr("class","dots")
        .attr("cx", function(d) { return x(d.x); })
        .attr("cy", function(d) { return y(d.y); });
    });

    d3.selectAll("#four")
    .on("click", function() {

    lr = linearRegression(dataFour.map(function(d) { return d.y; }), dataFour.map(function(d) { return d.x; }))
    d3.select(".summary")
      .html(function(d) { return "slope: " + lr.slope + "<br />" +
                                 " intercept: " + lr.intercept + "<br />" +
                                 " r2: " + lr.r2;})

      var max = d3.max(dataFour, function (d) { return d.x; });
      var myLine = svg.append("line")
                  .attr("x1", x(0))
                  .attr("y1", y(lr.intercept))
                  .attr("x2", x(max))
                  .attr("y2", y( (max * lr.slope) + lr.intercept ))
                  .style("stroke", "black"); 
                  
      d3.select("body")
        .select("h2")
        .text("Group IV")

      dots.data(dataFour)
        .transition()
        .duration(2000)
        .ease("elastic")
        .delay(function(d,i) { return i * 100; })
        .attr("cx", function(d) { return x(d.x); })
        .attr("cy", function(d) { return y(d.y); });
    });

  });


</script>

<div class="summary"></div>

</body>
</html>

data.tsv

group	x	y
I	10	8.04
I	8	6.95
I	13	7.58
I	9	8.81
I	11	8.33
I	14	9.96
I	6	7.24
I	4	4.26
I	12	10.84
I	7	4.82
I	5	5.68
II	10	9.14
II	8	8.14
II	13	8.74
II	9	8.77
II	11	9.26
II	14	8.1
II	6	6.13
II	4	3.1
II	12	9.13
II	7	7.26
II	5	4.74
III	10	7.46
III	8	6.77
III	13	12.74
III	9	7.11
III	11	7.81
III	14	8.84
III	6	6.08
III	4	5.39
III	12	8.15
III	7	6.42
III	5	5.73
IV	8	6.58
IV	8	5.76
IV	8	7.71
IV	8	8.84
IV	8	8.47
IV	8	7.04
IV	8	5.25
IV	19	12.5
IV	8	5.56
IV	8	7.91
IV	8	6.89

linearRegression.js

// http://trentrichardson.com/2010/04/06/compute-linear-regressions-in-javascript/

function linearRegression(y,x){

	var lr = {};
	var n = y.length;
	var sum_x = 0;
	var sum_y = 0;
	var sum_xy = 0;
	var sum_xx = 0;
	var sum_yy = 0;
	
	for (var i = 0; i < y.length; i++) {
		
		sum_x += x[i];
		sum_y += y[i];
		sum_xy += (x[i]*y[i]);
		sum_xx += (x[i]*x[i]);
		sum_yy += (y[i]*y[i]);
	} 
	
	lr['slope'] = (n * sum_xy - sum_x * sum_y) / (n*sum_xx - sum_x * sum_x);
	lr['intercept'] = (sum_y - lr.slope * sum_x)/n;
	lr['r2'] = Math.pow((n*sum_xy - sum_x*sum_y)/Math.sqrt((n*sum_xx-sum_x*sum_x)*(n*sum_yy-sum_y*sum_y)),2);
	
	return lr;
}