block by micahstubbs ea0a8c361ba849711628

Correlation Matrix V

Full Screen

Hexbins that aggregate all points in an area might be a better way to show the density of points than semi-transparent circles for each point in the data. This example draws a hexbin grid over the circles for a rough comparison. Each hexagon is shaded based on the number of points that fall in its area, with darker hexes containing more points.

Forked from Correlation Matrix IV and remixed with tiny hexbin plot by micahstubbs

Originally inspired by the Simple Correlation Matrix block from emeeks

index.html


<!DOCTYPE html>

<html lang="en">
<head>
<meta charset="utf-8" />
<title>Correlation Matrix V</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) + 
        ")" 
    });

/*
  ////////////////////////////////////////////////////////////
  // create the points for the hexbin layout for each 
  // small multiple chart
  var pointsObj = {};

  attributeMatrix.forEach(function(matrix, i) {
    if (matrix.a === matrix.b) {
      var points = [];
    } else {
      var points = data.map(function(d) {
        return [d[matrix.a], d[matrix.b]];
      })
    }
    pointsObj[i] = points;
  })

  console.log('pointsObj', pointsObj);
*/

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

  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)]) //"steelblue"
        .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]}}();