block by micahstubbs 03554bbf31c1df77cfde

Correlation Matrix VII

Full Screen

A hexbin correlation matrix (or pairs plot, if you prefer)

This iteration experiments with a white background and a color scale that starts at a faint shade of gray.

we declare a scale colors for picking the theme colors of the small charts and assign the faintGray hex color to an eponymous variable:

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

var faintGray = "#f0f0f0";

then we declare a different scale helpfully named color to color each hex on a scale of faint gray to the theme color for the current small chart. We use d3’s handy d3.interpolateLab L*a*b* color space interpolator.

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

This example also adds thin borders to the small multiple plots to distinguish them from the white background of the larger chart area.

Forked from Correlation Matrix VI 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 VII</title>
<link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
<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", "silver")
        .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", "white")
          .style("stroke", "silver");
/*     
      // 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([faintGray, 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", faintGray);
    }

  })


}

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