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