block by micahstubbs db57b9e8546b4b83a405

Correlation Matrix VI

Full Screen

Hexbins also mean fewer DOM elements on the page.

Just how many DOM Elements do we create to visualize the 2000 records in our diamonds.json dataset?

Pasting document.getElementsByTagName('*').length into the JavaScript console in Chrome’s DevTools tells us that there are 2279 elements on the page for this hexbins-only example.

In contrast, our one-circle-for-every-datapoint encoding method in Correlation Matrix IV creates 84072 DOM elements.

Forked from Correlation Matrix V by micahstubbs

Originally inspired by the Simple Correlation Matrix block from emeeks

DOM Element-counting snippet from this StackOverflow Answer

index.html


<!DOCTYPE html>

<html lang="en">
<head>
<meta charset="utf-8" />
<title>Correlation Matrix VI</title>
<style>
</style>
</head>
<body>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8" type="text/javascript"></script>
<script src="d3.hexbin.min.js"></script>
<script>

//These attributes could be derived from the data
attributes = ["carat","depth","table","price","x","y","z"];
attributeMatrix = [];

attributes.forEach(function (a, x) {
  attributes.forEach(function (b, y) {
    //create an n-by-n matrix based on pairs of attributes
    attributeMatrix.push({a: a, b: b, x: x, y: y})
  })
})

colors = d3.scale.ordinal()
  .range(["#827abf", "#f62150", "#6f89b6", "#f5e0b7", "#5b1e37", "#b9e3c5"]);

var faintGray = "#f0f0f0";

d3.select("body")
  .append("svg")
  .attr("height", 1140)
  .attr("width", 1140)

d3.json("diamonds.json", small_scatterplots);

function small_scatterplots(data) {

  console.log("data", data);
  console.log("attributeMatrix", attributeMatrix);

  //format data as numbers
  data.forEach(function (d) {
    attributes.forEach(function (att) {
      d[att] = Number(d[att])
    })
  })

  //create scales dynamically for each attribute's extent
  scale = {};
  attributes.forEach(function (att) {
    scale[att] = d3.scale.linear();
    attExtent = d3.extent(data, function (d) {return d[att]});
    scale[att].domain(attExtent).range([5,95]);
  })

  var height = 100;
  var width = 100;
  var padding = 7;

  var rows = attributes.length;
  var columns = rows;
  console.log('rows', rows);

  //bind the matrix array to a grid of g elements
  d3.select("svg")
    .selectAll("g")
    .data(attributeMatrix)
    .enter()
    .append("g")
    .attr("transform", function (d) {
      return "translate(" + 
        ((d.x * (width + padding)) + padding) + "," + 
        ((d.y * (height + padding)) + padding) + 
        ")" 
    });

  ////////////////////////////////////////////////////////////
  // setup the hexbin layout
  var hexbin = d3.hexbin()
    .size([width, height])
    .radius(5);

  var x = d3.scale.identity()
    .domain([0, width]);

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

  ////////////////////////////////////////////////////////////
  // draw the grid of small multiples charts
  d3.selectAll("g")
    .each(function (matrix, i) {

      //index i is only used for coloring
      var row = Math.floor(i % rows);
      var col = Math.floor(i / rows);
  
      //background/border
      d3.select(this).append("rect")
        .style("fill", "white")
        .style("stroke", "none")
        .style("stroke-width", 1)
        .attr("height", height)
        .attr("width", width);
        
      // if we are comparing an attribute with itself
      if (matrix.a === matrix.b) {
  
        // show a label
        d3.select(this)
          .append("text")
          .attr("x", 50)
          .style("text-anchor", "middle")
          .attr("y", 50)
          .style("font-family", "Roboto")
          .style("font-weight", 400)
          .text(matrix.a)
  
      } else {

        d3.select(this).select("rect")
          .style("fill", faintGray)
          .style("stroke", faintGray);
/*     
      // draw scatterplot points
      d3.select(this).selectAll("circle")
        .data(data)
        .enter()
        .append("circle")
        .attr("r", 2)
        .style("fill", colors(row + col))
        .attr("cx", function (d) {return scale[matrix.a](d[matrix.a])})
        .attr("cy", function (d) {return height - scale[matrix.b](d[matrix.b])})
*/      

      ////////////////////////////////////////////////////////////
      // draw hexbin scatterplot

      // create the points for the hexbin layout
      var points = data.map(function(d) {
        return [
          scale[matrix.a](d[matrix.a]), 
          height - scale[matrix.b](d[matrix.b])
        ];
      })

      var hexbinData = hexbin(points);

      var color = d3.scale.linear()
        .domain([0, d3.max(hexbinData, function(d) {
          return d.length;
        })])
        .range(["white", colors(row + col)]) 
        .interpolate(d3.interpolateLab);

      d3.select(this).append("clipPath")
        .attr("id", "clip")
        .append("rect")
          .attr("class", "mesh")
          .attr("width", width)
          .attr("height", height);

      d3.select(this)
        .append("g")
        .attr("clip-path", "url(#clip)")
        .selectAll(".hexagon")
          .data(hexbinData)
          .enter().append("path")
          .attr("class", "hexagon")
          .attr("d", hexbin.hexagon())
          .attr("transform", function(d) { 
            return "translate(" + d.x + "," + d.y + ")"; 
          })
          .style("fill", function(d) {
            return color(d.length) 
          })
          .style("stroke", "none");
    }

  })


}

</script>
</body>
</html>

d3.hexbin.min.js

!function(){d3.hexbin=function(){function u(n){var r={};return n.forEach(function(n,t){var a=s.call(u,n,t)/o,e=Math.round(a),c=h.call(u,n,t)/i-(1&e?.5:0),f=Math.round(c),l=a-e;if(3*Math.abs(l)>1){var v=c-f,g=f+(f>c?-1:1)/2,m=e+(e>a?-1:1),M=c-g,d=a-m;v*v+l*l>M*M+d*d&&(f=g+(1&e?1:-1)/2,e=m)}var j=f+"-"+e,p=r[j];p?p.push(n):(p=r[j]=[n],p.i=f,p.j=e,p.x=(f+(1&e?.5:0))*i,p.y=e*o)}),d3.values(r)}function a(r){var t=0,u=0;return n.map(function(n){var a=Math.sin(n)*r,e=-Math.cos(n)*r,i=a-t,o=e-u;return t=a,u=e,[i,o]})}var e,i,o,c=1,f=1,h=r,s=t;return u.x=function(n){return arguments.length?(h=n,u):h},u.y=function(n){return arguments.length?(s=n,u):s},u.hexagon=function(n){return arguments.length<1&&(n=e),"m"+a(n).join("l")+"z"},u.centers=function(){for(var n=[],r=0,t=!1,u=0;f+e>r;r+=o,t=!t,++u)for(var a=t?i/2:0,h=0;c+i/2>a;a+=i,++h){var s=[a,r];s.i=h,s.j=u,n.push(s)}return n},u.mesh=function(){var n=a(e).slice(0,4).join("l");return u.centers().map(function(r){return"M"+r+"m"+n}).join("")},u.size=function(n){return arguments.length?(c=+n[0],f=+n[1],u):[c,f]},u.radius=function(n){return arguments.length?(e=+n,i=2*e*Math.sin(Math.PI/3),o=1.5*e,u):e},u.radius(1)};var n=d3.range(0,2*Math.PI,Math.PI/3),r=function(n){return n[0]},t=function(n){return n[1]}}();