Here's some reusable code to compute and draw a correlation matrix from a set of data in JavaScript. Here's how it works:
forked from HarryStevens's block: Correlation Matrix
forked from willzjc's block: Correlation Matrix
xxxxxxxxxx
<html>
<head>
<style>
body {
margin: 0 auto;
display: table;
font-family: "Helvetica Neue", sans-serif;
}
rect.selected {
stroke: #000;
stroke-width: 2px;
}
.axis .domain {
display: none;
}
.axis .tick text.selected {
font-weight: bold;
font-size: 1.2em;
fill: #47ff63;
}
.axis .tick line.selected {
stroke: #47ff63;
}
.tip {
position: absolute;
font-size: .8em;
text-align: center;
text-shadow: -1px -1px 1px #ffffff, -1px 0px 1px #ffffff, -1px 1px 1px #ffffff, 0px -1px 1px #ffffff, 0px 1px 1px #ffffff, 1px -1px 1px #ffffff, 1px 0px 1px #ffffff, 1px 1px 1px #ffffff;
}
#legend {
margin-bottom: 10px;
}
#legend text {
font-size: .8em;
}
</style>
</head>
<body>
<div id="legend"></div>
<div id="grid"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/jeezy@1.12.11/lib/jeezy.min.js"></script>
<script src="https://unpkg.com/data2grid@1.0.0/build/data2grid.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/1.3.5/chroma.min.js"></script>
<script>
d3.select("body").append("div").attr("class", "tip").style("display", "none");
var data = [];
var cols = "abcdefghijklmnopqrstuvwxyz".split("");
for (var i = 0; i <= 30; i++){
var obj = {index: i};
cols.forEach(col => {
obj[col] = jz.num.randBetween(1, 100);
});
data.push(obj);
}
var corr = jz.arr.correlationMatrix(data, cols);
var extent = d3.extent(corr.map(function(d){ return d.correlation; }).filter(function(d){ return d !== 1; }));
var grid = data2grid.grid(corr);
var rows = d3.max(grid, function(d){ return d.row; });
var margin = {top: 20, bottom: 1, left: 20, right: 1};
var dim = d3.min([window.innerWidth * .9, window.innerHeight * .9]);
var width = dim - margin.left - margin.right, height = dim - margin.top - margin.bottom;
var svg = d3.select("#grid").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
var padding = .1;
var x = d3.scaleBand()
.range([0, width])
.paddingInner(padding)
.domain(d3.range(1, rows + 1));
var y = d3.scaleBand()
.range([0, height])
.paddingInner(padding)
.domain(d3.range(1, rows + 1));
var c = chroma.scale(["tomato", "white", "steelblue"])
.domain([extent[0], 0, extent[1]]);
var x_axis = d3.axisTop(y).tickFormat(function(d, i){ return cols[i]; });
var y_axis = d3.axisLeft(x).tickFormat(function(d, i){ return cols[i]; });
svg.append("g")
.attr("class", "x axis")
.call(x_axis);
svg.append("g")
.attr("class", "y axis")
.call(y_axis);
svg.selectAll("rect")
.data(grid, function(d){ return d.column_a + d.column_b; })
.enter().append("rect")
.attr("x", function(d){ return x(d.column); })
.attr("y", function(d){ return y(d.row); })
.attr("width", x.bandwidth())
.attr("height", y.bandwidth())
.style("fill", function(d){ return c(d.correlation); })
.style("opacity", 1e-6)
.transition()
.style("opacity", 1);
svg.selectAll("rect")
d3.selectAll("rect")
.on("mouseover", function(d){
d3.select(this).classed("selected", true);
d3.select(".tip")
.style("display", "block")
.html(d.column_x + ", " + d.column_y + ": " + d.correlation.toFixed(2));
var row_pos = y(d.row);
var col_pos = x(d.column);
var tip_pos = d3.select(".tip").node().getBoundingClientRect();
var tip_width = tip_pos.width;
var tip_height = tip_pos.height;
var grid_pos = d3.select("#grid").node().getBoundingClientRect();
var grid_left = grid_pos.left;
var grid_top = grid_pos.top;
var left = grid_left + col_pos + margin.left + (x.bandwidth() / 2) - (tip_width / 2);
var top = grid_top + row_pos + margin.top - tip_height - 5;
d3.select(".tip")
.style("left", left + "px")
.style("top", top + "px");
d3.select(".x.axis .tick:nth-of-type(" + d.column + ") text").classed("selected", true);
d3.select(".y.axis .tick:nth-of-type(" + d.row + ") text").classed("selected", true);
d3.select(".x.axis .tick:nth-of-type(" + d.column + ") line").classed("selected", true);
d3.select(".y.axis .tick:nth-of-type(" + d.row + ") line").classed("selected", true);
})
.on("mouseout", function(){
d3.selectAll("rect").classed("selected", false);
d3.select(".tip").style("display", "none");
d3.selectAll(".axis .tick text").classed("selected", false);
d3.selectAll(".axis .tick line").classed("selected", false);
});
// legend scale
var legend_top = 15;
var legend_height = 15;
var legend_svg = d3.select("#legend").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", legend_height + legend_top)
.append("g")
.attr("transform", "translate(" + margin.left + ", " + legend_top + ")");
var defs = legend_svg.append("defs");
var gradient = defs.append("linearGradient")
.attr("id", "linear-gradient");
var stops = [{offset: 0, color: "tomato", value: extent[0]}, {offset: .5, color: "white", value: 0}, {offset: 1, color: "steelblue", value: extent[1]}];
gradient.selectAll("stop")
.data(stops)
.enter().append("stop")
.attr("offset", function(d){ return (100 * d.offset) + "%"; })
.attr("stop-color", function(d){ return d.color; });
legend_svg.append("rect")
.attr("width", width)
.attr("height", legend_height)
.style("fill", "url(#linear-gradient)");
legend_svg.selectAll("text")
.data(stops)
.enter().append("text")
.attr("x", function(d){ return width * d.offset; })
.attr("dy", -3)
.style("text-anchor", function(d, i){ return i == 0 ? "start" : i == 1 ? "middle" : "end"; })
.text(function(d, i){ return d.value.toFixed(2) + (i == 2 ? ">" : ""); })
</script>
</body>
</html>
https://d3js.org/d3.v4.min.js
https://unpkg.com/jeezy@1.12.11/lib/jeezy.min.js
https://unpkg.com/data2grid@1.0.0/build/data2grid.min.js
https://cdnjs.cloudflare.com/ajax/libs/chroma-js/1.3.5/chroma.min.js