Skip to content

Instantly share code, notes, and snippets.

@ashenfad
Last active December 17, 2015 19:33
Show Gist options
  • Save ashenfad/923522ba4dda524d0b9f to your computer and use it in GitHub Desktop.
Save ashenfad/923522ba4dda524d0b9f to your computer and use it in GitHub Desktop.
Dynamic Heatmap - Iris

A 2D/3D heatmap of the iris dataset.

Click and drag to zoom. Single click to zoom out.

var candidateIntervals = [2, 2.5, 5, 10];
function prettyRange (initRange, size) {
var minimum = initRange[0];
var maximum = initRange[1];
if (minimum != maximum) {
var diff = (maximum - minimum) / size;
var tens = Math.floor(Math.log10(diff));
var tscale = Math.pow(10, tens);
var rem = diff / tscale;
var minIndex;
var minDiff = 1E20;
for (i in candidateIntervals) {
var c = candidateIntervals[i];
var cDiff = c - rem;
if (cDiff > 0 && cDiff < minDiff) {
minDiff = cDiff;
minIndex = i;
}
}
var interval = candidateIntervals[minIndex] * tscale;
var newMin = interval * Math.floor(minimum / interval);
var newMax = newMin + interval * size;
if (newMax < maximum) {
return prettyRange([newMin, newMax], size);
} else if (newMax == maximum) {
if (newMax == 0) {
newMax = 1E-8;
} else {
newMax = newMax * 1.000001;
}
return prettyRange([newMin, newMax], size);
} else {
return [newMin, newMax];
}
} else {
return [minimum, maximum];
}
}
function truncate (val) {
return Math.round(1E5 * val) / 1E5;
}
function pickEdges(range, size) {
var newRange = prettyRange(range, size);
var step = truncate((newRange[1] - newRange[0]) / size);
var edges = [];
var truncatedEnd = truncate(range[1]);
for (var i = 0; i <= size; i++) {
var prevEdge = truncate(newRange[0] + (step * (i - 1)));
var edge = truncate(prevEdge + step);
if (prevEdge <= truncatedEnd) {
edges.push(edge);
}
}
return {step: step, edges: edges, range: [edges[0], edges[edges.length - 1]]};
}
function findField(selection, data) {
if (selection.id == null) return null;
var fieldIds = data.fields.map(function (field) {return field.id;});
var column = fieldIds.indexOf(selection.id);
data.fields[column].column = column;
return data.fields[column];
}
function makeAxisIndexer (selection, data, numBins, catBins) {
var field = findField(selection, data);
var range = selection.range;
if (range == null) {
if (field.optype == "numeric") {
range = [field.minimum, field.maximum];
} else {
range = field.categories;
if (range.length > catBins) {
range = range.slice(0, catBins);
}
}
}
var axis;
if (field.optype == "numeric") {
axis = pickEdges(range, numBins);
axis.binCount = axis.edges.length - 1;
var edges = axis.edges;
var minRange = edges[0];
var maxRange = edges[edges.length - 1];
var step = axis.step;
axis.indexer = function (val) {
var index = -1;
if (val != null && val >= minRange && val <= maxRange) {
index = Math.floor(truncate((val - minRange) / step));
}
return index;
}
} else {
axis = {range: range, binCount: range.length};
var indexMap = field.categories.map(function (cat) { return range.indexOf(cat)});
axis.indexer = function (val) {
var index = -1;
if (val != null) {
index = indexMap[val];;
}
return index;
}
}
axis.optype = field.optype;
axis.column = field.column;
return axis;
}
function genHeatmap(data, xSelection, ySelection, cSelection, numBins, catBins) {
var colorField = findField(cSelection, data);
var xIndexInfo = makeAxisIndexer(xSelection, data, numBins, catBins);
var xNumeric = xIndexInfo.optype == "numeric";
var xIndexer = xIndexInfo.indexer;
var yIndexInfo = makeAxisIndexer(ySelection, data, numBins, catBins);
var yNumeric = yIndexInfo.optype == "numeric";
var yIndexer = yIndexInfo.indexer;
var counts = [];
for (var i = 0; i < xIndexInfo.binCount; i++) {
counts.push(new Int32Array(yIndexInfo.binCount));
}
var fullCatCounts = null;
var catCounts = null;
var sums = null;
var targetCat = null;
if (cSelection.targetCat != null) {
targetCat = colorField.categories.indexOf(cSelection.targetCat);
}
if (colorField != null) {
if (colorField.optype == "numeric") {
sums = [];
for (var i = 0; i < xIndexInfo.binCount; i++) {
sums.push(new Float64Array(yIndexInfo.binCount));
}
} else {
if (targetCat != null) {
catCounts = [];
for (var i = 0; i < xIndexInfo.binCount; i++) {
catCounts.push(new Int32Array(yIndexInfo.binCount));
}
} else {
var cats = colorField.categories.length;
fullCatCounts = [];
for (var i = 0; i < xIndexInfo.binCount; i++) {
var row = [];
for (var j = 0; j < yIndexInfo.binCount; j++) {
row.push(new Int32Array(cats));
}
fullCatCounts.push(row);
}
}
}
}
var xData = data.data[xIndexInfo.column];
var yData = data.data[yIndexInfo.column];
var cData = null;
if (colorField != null) {
cData = data.data[colorField.column];
}
var maxBinCount = 0;
var totalCount = 0;
var rowCounts = new Int32Array(xIndexInfo.binCount);
var colCounts = new Int32Array(yIndexInfo.binCount);
for (i in xData) {
var x = xIndexer(xData[i]);
var y = yIndexer(yData[i]);
if (x >= 0 && x < xIndexInfo.binCount && y >= 0 && y < yIndexInfo.binCount) {
if (cData != null && cData[i] == null) {
continue;
}
counts[x][y]++;
rowCounts[x]++;
colCounts[y]++;
totalCount++;
maxBinCount = Math.max(maxBinCount, counts[x][y]);
if (cData != null) {
if (sums != null && cData[i]) {
sums[x][y] += cData[i];
}
if (catCounts != null && cData[i] == targetCat) {
catCounts[x][y]++;
}
if (fullCatCounts != null) {
fullCatCounts[x][y][cData[i]]++;
}
}
}
}
var result = {x: xIndexInfo,
y: yIndexInfo,
c: colorField,
totalCount: totalCount,
counts: counts};
if (sums != null) {
result.sums = sums;
}
if (catCounts != null) {
result.catCounts = catCounts;
}
if (fullCatCounts != null) {
catCounts = [];
var mostCommon = [];
for (var x = 0; x < xIndexInfo.binCount; x++) {
mostCommon.push(new Int16Array(yIndexInfo.binCount));
catCounts.push(new Int32Array(yIndexInfo.binCount));
for (var y = 0; y < yIndexInfo.binCount; y++) {
var dist = fullCatCounts[x][y];
var maxCat = -1;
var maxCount = -1;
for (var z = 0; z < dist.length; z++) {
if (dist[z] > maxCount) {
maxCat = z;
maxCount = dist[z];
}
}
mostCommon[x][y] = maxCat;
catCounts[x][y] = maxCount;
}
}
result.catCounts = catCounts;
result.mostCommon = mostCommon;
}
return result;
}
function differenceSum(counts, xNumeric, yNumeric) {
var rows = counts.length;
var cols = counts[0].length;
var total = 0;
var diff = 0;
for (var i = 0; i < rows; i++) {
for (var j = 0; j < cols; j++) {
var pop = counts[i][j];
total += pop;
if (xNumeric) {
if (i < rows - 1) {
diff += Math.abs(pop - counts[i+1][j]);
} else {
diff += pop;
}
if (xNumeric && i > 0) {
diff += Math.abs(pop - counts[i-1][j]);
} else {
diff += pop;
}
}
if (yNumeric) {
if (j < cols - 1) {
diff += Math.abs(pop - counts[i][j+1]);
} else {
diff += pop;
}
if (j > 0) {
diff += Math.abs(pop - counts[i][j-1]);
} else {
diff += pop;
}
}
}
}
return diff / total;
}
function heatmap(data, xSelection, ySelection, cSelection, opts) {
var xNumeric = findField(xSelection, data).optype == "numeric";
var yNumeric = findField(ySelection, data).optype == "numeric";
if (opts.numBins != null || (!xNumeric && !yNumeric)) {
return genHeatmap(data, xSelection, ySelection, cSelection,
opts.numBins, opts.catBins);
} else {
var binCandidates;
if (xNumeric && yNumeric) {
binCandidates = [256, 128, 64, 32, 16, 8, 4];
} else {
binCandidates = [128, 64, 32, 16, 8, 4];
}
var minScore = null;
var bestResult = null;
for (i in binCandidates) {
var result = genHeatmap(data, xSelection, ySelection, cSelection,
binCandidates[i], opts.catBins);
var score = differenceSum(result.counts, xNumeric, yNumeric);
if (minScore == null || minScore >= score) {
minScore = score;
bestResult = result;
}
}
}
return bestResult;
}
function cartesianProduct() {
var args = [].slice.call(arguments);
var end = args.length - 1;
var result = [];
function addTo(curr, start) {
var first = args[start];
var last = (start === end);
for (var i = 0; i < first.length; ++i) {
var copy = curr.slice();
copy.push(first[i]);
if (last) {
result.push(copy);
} else {
addTo(copy, start + 1);
}
}
}
if (args.length) {
addTo([], 0);
} else {
result.push([]);
}
return result;
}
function range (v) {
return Array.apply(null, Array(v)).map(function (_, i) {return i;})
}
function binList (grid) {
return cartesianProduct(range(grid.x.binCount), range(grid.y.binCount));
}
</script>
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.domain {
stroke: none;
fill: none;
}
.field {
cursor: pointer;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.brush .extent {
stroke: #fff;
fill-opacity: .125;
shape-rendering: crispEdges;
}
svg {
-webkit-user-select: none; /* webkit (safari, chrome) browsers */
-moz-user-select: none; /* mozilla browsers */
-khtml-user-select: none; /* webkit (konqueror) browsers */
-ms-user-select: none; /* IE10+ */
}
#shading-slider {
width: 90px;
}
.selector {
margin-bottom: 15px;
}
#axis {
position:absolute;
}
#heatmap {
}
div#pdp {
float: left;
margin-right: 5px;
}
div#sidebar {
padding-top: 20px;
}
div.field-info {
margin-bottom: 10px;
}
div.field-name {
color: #999;
margin-right: 10px;
}
div.field-value {
}
</style>
<body oncontextmenu="return false;">
<div id="pdp"></div>
<div id="sidebar">
<form id="controls-form">
<div class="selector">
<div>X-Axis</div>
<select id="x-select"></select>
</div>
<div class="selector">
<div>Y-Axis</div>
<select id="y-select"></select>
</div>
<div class="selector">
<div>Color Field</div>
<select id="color-select"></select>
</div>
<div class="selector">
<div>Focus Category</div>
<select id="focus-select"></select>
</div>
<div class="selector">
<div>Density Shading</div>
<input id="shading-slider" type="range" min="0" max="1" step="0.1" value="0.5"/>
</div>
<div class="selector">
<div>Max Bins</div>
<select id="bins-select">
<option value="dynamic" selected>Dynamic</option>
<option value="256">256</option>
<option value="128">128</option>
<option value="64">64</option>
<option value="32"">32</option>
<option value="16">16</option>
<option value="8">8</option>
</select>
</div>
</form>
<div id="x-field" class="field-info">
<div id="x-name" class="field-name"></div>
<div id="x-value" class="field-value"></div>
</div>
<div id="y-field" class="field-info">
<div id="y-name" class="field-name"></div>
<div id="y-value" class="field-value"></div>
</div>
<div id="c-field" class="field-info">
<div id="c-name" class="field-name"></div>
<div id="c-value" class="field-value"></div>
</div>
</div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="iris.js"></script>
<script src="data.js"></script>
<script src="grid.js"></script>
<script>
function toggle_option (selectId, index, disabled) {
document.getElementById(selectId)
.getElementsByTagName("option")[index].disabled = disabled;
}
function getParam(key) {
if(key=(new RegExp('[?&]'+encodeURIComponent(key)+'=([^&]*)'))
.exec(location.search))
return decodeURIComponent(key[1]);
}
function setParam(key, value) {
key = encodeURI(key); value = encodeURI(value);
var s = document.location.search;
var kvp = key+"="+value;
var r = new RegExp("(&|\\?)"+key+"=[^\&]*");
s = s.replace(r,"$1"+kvp);
if(!RegExp.$1) {s += (s.length>0 ? '&' : '?') + kvp;};
window.history.replaceState({}, "", s);
}
function removeParam(key) {
var sourceURL = document.location.search;
var rtn = sourceURL.split("?")[0],
param,
params_arr = [],
queryString = (sourceURL.indexOf("?") !== -1) ? sourceURL.split("?")[1] : "";
if (queryString !== "") {
params_arr = queryString.split("&");
for (var i = params_arr.length - 1; i >= 0; i -= 1) {
param = params_arr[i].split("=")[0];
if (param === key) {
params_arr.splice(i, 1);
}
}
rtn = rtn + "?" + params_arr.join("&");
}
window.history.replaceState({}, "", rtn);
}
var margin = {bottom: 40, left: 50, right: 20, top: 20};
var width = 640 - margin.left;
var height = 480 - margin.bottom;
var axisSVG = d3.select("#pdp").append("svg")
.attr("id", "axis")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var canvas = d3.select("#pdp").append("canvas")
.attr("id", "heatmap")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top);
var context = canvas.node().getContext("2d");
d3.json("iris-dataset.json", function(error, dataset) {
if ('dataset' in dataset) {
dataset = dataset.dataset;
}
var fields = dataset.fields;
if (!Array.isArray(fields)){
var fieldsArr = [];
for (fid in fields) {
var field = fields[fid];
field.id = fid;
fieldsArr.push(field);
}
fields = fieldsArr;
}
var maxNumBins = 128;
var maxCatBins = 16;
var x = getParam("x");
if (x == null) x = 2;
x = Number(x);
setParam("x", x);
var y = getParam("y");
if (y == null) y = 3;
y = Number(y);
setParam("y", y);
var c = getParam("c");
if (c == null) c = fields.length - 1;
c = Number(c);
setParam("c", c);
var targetFields = [fields[x], fields[y]];
var xSelection = {id: fields[x].id};
var ySelection = {id: fields[y].id};
var cSelection = {};
if (c >= 0) cSelection.id = fields[c].id;
var opts = {maxNumBins: maxNumBins, maxCatBins: maxCatBins};
var binCanvasHeight;
var binCanvasWidth;
var hm;
var colorSelector = d3.select("#color-select");
colorSelector.append("option").attr("value", "#noValue").text("None");
colorSelector.selectAll(".c-option")
.data(fields).enter()
.append("option")
.attr("class", "c-option")
.attr("value", function(d) {return d.id;})
.text(function(d) {return d.name;});
document.getElementById("color-select").selectedIndex = c + 1;
colorSelector.on("change",
function () {
c = this.selectedIndex - 1;
setParam("c", c);
updateColorSelector();
resetFocusSelector();
updateHeatMap();
updateColorFn();
redraw(true);
});
updateColorSelector();
var focusSelector = d3.select("#focus-select");
focusSelector.append("option").attr("value", "#noValue").text("Most common");
focusSelector.on("change",
function () {
if (this.selectedIndex > 0) {
cSelection.targetCat =
fields[c].summary.categories[this.selectedIndex - 1][0];
} else {
delete cSelection.targetCat;
}
setParam("focus", this.selectedIndex - 1);
updateHeatMap();
updateColorFn();
redraw(false);
});
updateFocusSelector();
var focusParam = getParam("focus");
if (focusParam == null) {
focusParam = -1;
}
focusParam = Number(focusParam);
document.getElementById("focus-select").selectedIndex = focusParam + 1;
if (focusParam >= 0) {
cSelection.targetCat = fields[c].summary.categories[focusParam][0];
}
var binsIndex = getParam("bins");
var binSelect = document.getElementById("bins-select");
if (binsIndex == null) binsIndex = 0;
binsIndex = Number(binsIndex);
binSelect.selectedIndex = binsIndex;
if (binsIndex > 0) {
opts.numBins = Number(binSelect.options[binsIndex].value);
}
d3.select("#bins-select")
.on("change",
function () {
if (this.value == "dynamic") {
delete opts.numBins;
} else {
opts.numBins = this.value;
}
setParam("bins", this.selectedIndex);
updateHeatMap();
updateColorFn();
redraw(true);
});
updateHeatMap();
updateColorFn();
d3.select("#x-name").text(fields[x].name);
d3.select("#x-value").text("-");
d3.select("#y-name").text(fields[y].name);
d3.select("#y-value").text("-");
d3.select("#c-name").text("Total Count");
d3.select("#objective-value").text("-");
var xSelector = d3.select("#x-select");
xSelector.selectAll(".x-option")
.data(fields).enter()
.append("option")
.attr("class", "x-option")
.attr("value", function(d) {return d.id;})
.text(function(d) {return d.name;});
document.getElementById("x-select").selectedIndex = x;
toggle_option("x-select", y, true);
xSelector.on("change",
function () {
var oldX = x;
var newX = this.selectedIndex;
toggle_option("y-select", oldX, false);
toggle_option("y-select", newX, true);
document.getElementById("y-select")
.getElementsByTagName("option")[newX].disabled = true;
x = newX;
setParam("x", x);
d3.select("#x-name").text(fields[x].name);
targetFields[0] = fields[x];
xSelection = {id: fields[x].id};
delete ySelection.range;
updateHeatMap();
redraw(true);
});
var ySelector = d3.select("#y-select");
ySelector.selectAll(".y-option")
.data(fields).enter()
.append("option")
.attr("class", "y-option")
.attr("value", function(d) {return d.id;})
.text(function(d) {return d.name;});
document.getElementById("y-select").selectedIndex = y;
toggle_option("y-select", x, true);
ySelector.on("change",
function () {
var oldY = y;
var newY = this.selectedIndex;
toggle_option("x-select", oldY, false);
toggle_option("x-select", newY, true);
document.getElementById("x-select")
.getElementsByTagName("option")[newY].disabled = true;
y = newY;
setParam("y", y);
d3.select("#y-name").text(fields[y].name);
targetFields[1] = fields[y];
ySelection = {id: fields[y].id};
delete xSelection.range;
updateHeatMap();
redraw(true);
});
var shading = getParam("shading");
if (shading == null) {
shading = 3;
}
shading = Number(shading) / 10;
document.getElementById("shading-slider").value = shading;
d3.select("#shading-slider")
.on("input",
function () {
shading = this.value;
setParam("shading", shading * 10);
updateDensityFn();
updateCells();
});
var brushX = d3.scale.identity().domain([0, width]);
var brushY = d3.scale.identity().domain([0, height]);
function brushed() {
brushExtent = brush.extent();
}
function sortNumber(a,b) {
return a - b;
}
function findRange(axis, canvasRange, scale, isX) {
var newRange = [];
if (axis.optype == "numeric") {
newRange.push(scale.invert(canvasRange[0]));
newRange.push(scale.invert(canvasRange[1]));
newRange.sort(sortNumber);
} else {
var tempRange = [];
if (isX) {
tempRange.push(Math.floor(canvasRange[0] / binCanvasWidth));
tempRange.push(Math.floor(canvasRange[1] / binCanvasWidth));
} else {
tempRange.push(Math.floor((height - canvasRange[0]) / binCanvasHeight));
tempRange.push(Math.floor((height - canvasRange[1]) / binCanvasHeight));
}
tempRange.sort(sortNumber);
newRange = axis.range.slice(tempRange[0], tempRange[1] + 1);
}
return newRange;
}
function brushended() {
if (!d3.event.sourceEvent) return; // only transition after input
var canvasX = [brushExtent[0][0], brushExtent[1][0]];
var canvasY = [brushExtent[0][1], brushExtent[1][1]];
if (canvasX[0] == canvasX[1] || canvasY[0] == canvasY[1]) {
delete xSelection.range;
delete ySelection.range;
} else {
xSelection.range = findRange(hm.x, canvasX, xScale, true);
ySelection.range = findRange(hm.y, canvasY, yScale, false);
}
d3.select(this).call(brush.extent([[0, 0], [0, 0]]));
updateHeatMap();
updateColorFn();
redraw(true);
}
var gx, gy;
var xAxis, yAxis;
var xScale;
var yScale;
var predictor;
var densityFn;
var pMin, pMax;
makeAxis();
updateColorSelector();
redraw(true);
var brushExtent;
var brush = d3.svg.brush()
.x(brushX)
.y(brushY)
.on("brush", brushed)
.on("brushend", brushended);
axisSVG.append("g")
.attr("class", "brush")
.on("mousemove",
function(d) {
var coords = d3.mouse(this);
var inputs = [];
var gridIndex = coordsToGridIndex(coords);
var gx = gridIndex[0];
var gy = gridIndex[1];
if (hm.x.optype == "numeric") {
inputs.push(xScale.invert(coords[0]));
} else {
inputs.push(hm.x.range[gx]);
}
if (hm.y.optype == "numeric") {
inputs.push(yScale.invert(coords[1]));
} else {
inputs.push(hm.y.range[gy]);
}
var output;
var xOut;
var yOut;
if (gx < hm.x.binCount && gy < hm.y.binCount) {
if (c < 0) {
output = prettyVal(hm.counts[gx][gy]);
} else {
if (fields[c].optype == "numeric") {
var sum = hm.sums[gx][gy];
var pop = hm.counts[gx][gy];
if (pop == 0) {
output = "No data";
} else {
output = prettyVal(sum / pop) + " (" + pop + " points)";
}
} else {
var cat;
if (cSelection.targetCat == null) {
cat = hm.c.categories[hm.mostCommon[gx][gy]];
} else {
cat = cSelection.targetCat;
}
var total = hm.counts[gx][gy];
if (total > 0) {
var catCount = hm.catCounts[gx][gy];
if (catCount == 0) {
cat = "---";
}
output = cat + " (" + catCount + " of " + total + ")";
} else {
output = "No data";
}
}
}
xOut = binText(inputs[0], hm.x);
yOut = binText(inputs[1], hm.y);
} else {
output = "-";
xOut = "-";
yOut = "-";
}
d3.select("#x-value").text(prettyVal(xOut));
d3.select("#y-value").text(prettyVal(yOut));
d3.select("#c-value").text(output);
})
.on("mouseout", function(d) {
d3.select("#x-value").text("-");
d3.select("#y-value").text("-");
d3.select("#c-value").text("-");
})
.call(brush)
.call(brush.event);
function updateColorSelector() {
if (c >= 0) {
var text = fields[c].name;
if (fields[c].optype == "numeric") {
text += " (mean)";
}
d3.select("#c-name").text(text);
cSelection = {id: fields[c].id};
} else {
d3.select("#c-name").text("Total Count");
cSelection = {};
}
}
function resetFocusSelector() {
updateFocusSelector();
document.getElementById("focus-select").selectedIndex = 0;
setParam("focus", -1);
}
function updateFocusSelector() {
if (c >= 0 && fields[c].optype == "categorical") {
var categories = fields[c].summary.categories.map(function (d) {return d[0];});
focusSelector.selectAll(".f-option").remove();
focusSelector.selectAll(".f-option")
.data(categories).enter()
.append("option")
.attr("class", "f-option")
.attr("value", function(d) {return categories.indexOf(d);})
.text(function(d) {return d});
document.getElementById("focus-select").disabled = false;
} else {
focusSelector.selectAll(".f-option").remove();
document.getElementById("focus-select").disabled = true;
}
}
function binText(val, axis) {
if (axis.optype == "numeric") {
var index = axis.indexer(val);
var edges = axis.edges;
return edges[index] + " to " + edges[index + 1];
} else {
return val;
}
}
function updateDensityMax() {
pMax = -1E20;
var counts;
if (cSelection.targetCat == null) {
counts = hm.counts;
} else {
counts = hm.catCounts;
}
var binIds = binList(hm);
for (var i = 0; i < binIds.length; i++) {
var p = counts[binIds[i][0]][binIds[i][1]];
pMax = Math.max(pMax, p);
}
}
function updateDensityFn() {
var densityScale;
if (shading == 0) {
densityScale = function (d) {return 1};
} else {
densityScale = d3.scale.pow().exponent(shading).domain([0, pMax]);
}
if (cSelection.targetCat == null) {
counts = hm.counts;
} else {
counts = hm.catCounts;
}
densityFn = function (d) {
return densityScale(counts[d[0]][d[1]]);
};
}
var colorFn;
function updateColorFn() {
if (c < 0) {
var white = d3.rgb("#fff");
colorFn = function (d) {return white};
} else if (fields[c].optype == "categorical") {
var catColors;
if (hm.c.categories.length > 10) {
catColors = d3.scale.category20();
} else {
catColors = d3.scale.category10();
}
catColors.domain(hm.c.categories);
colorFn = function (d) {
var cat;
if (hm.catCounts[d[0]][d[1]] == 0) {
return d3.rgb("#000");
} else if (cSelection.targetCat == null) {
cat = hm.c.categories[hm.mostCommon[d[0]][d[1]]]
} else {
cat = cSelection.targetCat;
}
return catColors(cat);
};
} else {
var meanMin = 1E20;
var meanMax = -1E20;
var binIds = binList(hm);
for (var i = 0; i < binIds.length; i++) {
var sum = hm.sums[binIds[i][0]][binIds[i][1]];
var pop = hm.counts[binIds[i][0]][binIds[i][1]];
if (pop > 0) {
var mean = sum / pop;
meanMin = Math.min(meanMin, mean);
meanMax = Math.max(meanMax, mean);
}
}
var numColors = d3.scale.linear().domain([meanMin, meanMax]);
numColors = numColors.range(["#11f", "#f11"]);
var black = d3.rgb("#000");
colorFn = function (d) {
var sum = hm.sums[d[0]][d[1]];
var pop = hm.counts[d[0]][d[1]];
if (pop > 0) {
return numColors(sum / pop);
} else {
return black;
}
};
}
}
function redraw(redrawAxis) {
updateDensityMax();
updateDensityFn();
if (redrawAxis) {
updateScales();
updateAxis();
}
updateCells();
}
function makeAxis() {
gy = axisSVG.append("g").attr("class", "y-axis");
gx = axisSVG.append("g")
.attr("class", "x-axis")
.attr("transform", "translate(" + 0 + "," + height + ")");
}
function updateAxis() {
var currentY = gy.transition().duration(700).call(yAxis);
if (targetFields[1].optype == "categorical") {
currentY.selectAll("text")
.attr("y", (binCanvasHeight / 2) - 10)
.attr("x", 4)
.style("fill", "#fff")
.style("stroke", "#000")
.style("stroke-width", 0.3)
.style("font-size", "13")
.style("font-weight", "bolder")
.style("font-family", "Monospace")
.style("stroke-linecap", "butt")
.style("stroke-linejoin", "miter")
.style("text-anchor", "start");
}
var currentX = gx.transition().duration(700).call(xAxis);
if (targetFields[0].optype == "categorical") {
currentX.selectAll("text")
.attr("y", (binCanvasWidth / 2) - 15)
.attr("x", 4)
.attr("transform", "rotate(-90)")
.style("fill", "#fff")
.style("stroke", "#000")
.style("stroke-width", 0.3)
.style("font-size", "13")
.style("font-weight", "bolder")
.style("font-family", "Monospace")
.style("stroke-linecap", "butt")
.style("stroke-linejoin", "miter")
.style("text-anchor", "start");
}
}
function customAxisFormat(d) {
return d3.format("s")(Math.round(d * 1E4) / 1E4);
}
function updateScales () {
if (targetFields[0].optype == "numeric") {
xScale = d3.scale.linear()
.range([0, width])
.domain([hm.x.range[0],
hm.x.range[1]]);
} else {
xScale = d3.scale.ordinal()
.domain(hm.x.range)
.rangePoints([0, width], 1);
}
if (targetFields[1].optype == "numeric") {
yScale = d3.scale.linear()
.range([height, 0])
.domain([hm.y.range[0],
hm.y.range[1]]);
} else {
yScale = d3.scale.ordinal()
.domain(hm.y.range)
.rangePoints([height, 0], 1);
}
xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
if (targetFields[0].optype == "numeric") {
xAxis.tickFormat(customAxisFormat);
}
if (targetFields[1].optype == "numeric") {
yAxis.tickFormat(customAxisFormat);
}
}
function prettyVal(d) {
if (typeof d === 'string' || d instanceof String) {
return d;
} else {
return d = Math.round(d * 1000) / 1000;
}
}
function densityColorShift(color, loc) {
var density = densityFn(loc);
color = d3.rgb(color);
color.r = Math.round(color.r * density);
color.g = Math.round(color.g * density);
color.b = Math.round(color.b * density);
return color;
}
function updateCells() {
var cxFn = canvasXFn();
var cyFn = canvasYFn();
var maxBins = Math.max(hm.x.binCount, hm.y.binCount);
var alpha = Math.pow(Math.max((220 - maxBins), 0) / 256, 2);
var lineColor = "rgba(0, 0, 0, " + alpha + ")";
context.strokeStyle=lineColor;
binList(hm).forEach(function(d, i) {
context.beginPath();
context.rect(cxFn(d) + margin.left,
cyFn(d) + margin.top,
binCanvasWidth + 1,
binCanvasHeight + 1);
context.fillStyle=densityColorShift(colorFn(d), d);
context.fill();
/* context.stroke(); */
context.closePath();
});
}
function updateHeatMap() {
hm = heatmap(data, xSelection, ySelection, cSelection, opts);
binCanvasWidth = width / hm.x.binCount;
binCanvasHeight = height / hm.y.binCount;
}
function coordsToGridIndex (coords) {
var x = Math.floor(coords[0] / binCanvasWidth);
var y = Math.floor((height - coords[1]) / binCanvasHeight);
return [x, y];
}
function canvasXFn () {
return function(binId) {return binId[0] * binCanvasWidth};
}
function canvasYFn () {
return function(binId) {
return height - binCanvasHeight - (binId[1] * binCanvasHeight)
};
}
});
</script>
</body>
{
"dataset" : {
"term_limit" : 500,
"missing_tokens" : [ "", "NaN", "NULL", "N/A", "null", "-", "#REF!", "#VALUE!", "?", "#NULL!", "#NUM!", "#DIV/0", "n/a", "#NAME?", "NIL", "nil", "na", "#N/A", "NA" ],
"locale" : "en_US",
"fields" : [ {
"id" : "000000",
"preferred" : true,
"summary" : {
"skewness" : 0.31175,
"splits" : [ 4.51526, 4.67252, 4.81113, 4.89582, 4.96139, 5.01131, 5.05992, 5.11148, 5.18177, 5.35681, 5.44129, 5.5108, 5.58255, 5.65532, 5.71658, 5.77889, 5.85381, 5.97078, 6.05104, 6.13074, 6.23023, 6.29578, 6.35078, 6.41459, 6.49383, 6.63013, 6.70719, 6.79218, 6.92597, 7.20423, 7.64746 ],
"mean" : 5.84333,
"sum_squares" : 5223.85,
"bins" : [ [ 4.3, 1 ], [ 4.425, 4 ], [ 4.6, 4 ], [ 4.77143, 7 ], [ 4.9625, 16 ], [ 5.1, 9 ], [ 5.2, 4 ], [ 5.3, 1 ], [ 5.4, 6 ], [ 5.5, 7 ], [ 5.6, 6 ], [ 5.7, 8 ], [ 5.8, 7 ], [ 5.9, 3 ], [ 6, 6 ], [ 6.1, 6 ], [ 6.2, 4 ], [ 6.3, 9 ], [ 6.4, 7 ], [ 6.5, 5 ], [ 6.6, 2 ], [ 6.7, 8 ], [ 6.8, 3 ], [ 6.9, 4 ], [ 7, 1 ], [ 7.1, 1 ], [ 7.2, 3 ], [ 7.3, 1 ], [ 7.4, 1 ], [ 7.6, 1 ], [ 7.7, 4 ], [ 7.9, 1 ] ],
"maximum" : 7.9,
"missing_count" : 0,
"variance" : 0.68569,
"median" : 5.8,
"population" : 150,
"minimum" : 4.3,
"standard_deviation" : 0.82807,
"kurtosis" : -0.57357,
"sum" : 876.5
},
"datatype" : "double",
"order" : 0,
"optype" : "numeric",
"name" : "sepal length",
"column_number" : 0
}, {
"id" : "000001",
"preferred" : true,
"summary" : {
"skewness" : 0.31577,
"counts" : [ [ 2, 1 ], [ 2.2, 3 ], [ 2.3, 4 ], [ 2.4, 3 ], [ 2.5, 8 ], [ 2.6, 5 ], [ 2.7, 9 ], [ 2.8, 14 ], [ 2.9, 10 ], [ 3, 26 ], [ 3.1, 11 ], [ 3.2, 13 ], [ 3.3, 6 ], [ 3.4, 12 ], [ 3.5, 6 ], [ 3.6, 4 ], [ 3.7, 3 ], [ 3.8, 6 ], [ 3.9, 2 ], [ 4, 1 ], [ 4.1, 1 ], [ 4.2, 1 ], [ 4.4, 1 ] ],
"mean" : 3.05733,
"sum_squares" : 1430.4,
"maximum" : 4.4,
"missing_count" : 0,
"variance" : 0.18998,
"median" : 3,
"population" : 150,
"minimum" : 2,
"standard_deviation" : 0.43587,
"kurtosis" : 0.18098,
"sum" : 458.6
},
"datatype" : "double",
"order" : 1,
"optype" : "numeric",
"name" : "sepal width",
"column_number" : 1
}, {
"id" : "000002",
"preferred" : true,
"summary" : {
"skewness" : -0.27213,
"splits" : [ 1.25138, 1.32426, 1.37171, 1.40962, 1.44567, 1.48173, 1.51859, 1.56301, 1.6255, 1.74645, 3.23033, 3.675, 3.94203, 4.0469, 4.18243, 4.34142, 4.45309, 4.51823, 4.61771, 4.72566, 4.83445, 4.93363, 5.03807, 5.1064, 5.20938, 5.43979, 5.5744, 5.6646, 5.81496, 6.02913, 6.38125 ],
"mean" : 3.758,
"sum_squares" : 2582.71,
"bins" : [ [ 1, 1 ], [ 1.16667, 3 ], [ 1.3, 7 ], [ 1.4, 13 ], [ 1.5, 13 ], [ 1.6, 7 ], [ 1.7, 4 ], [ 1.9, 2 ], [ 3, 1 ], [ 3.3, 2 ], [ 3.5, 2 ], [ 3.6, 1 ], [ 3.75, 2 ], [ 3.9, 3 ], [ 4.0375, 8 ], [ 4.23333, 6 ], [ 4.46667, 12 ], [ 4.6, 3 ], [ 4.74444, 9 ], [ 4.94444, 9 ], [ 5.1, 8 ], [ 5.25, 4 ], [ 5.46, 5 ], [ 5.6, 6 ], [ 5.75, 6 ], [ 5.95, 4 ], [ 6.1, 3 ], [ 6.3, 1 ], [ 6.4, 1 ], [ 6.6, 1 ], [ 6.7, 2 ], [ 6.9, 1 ] ],
"maximum" : 6.9,
"missing_count" : 0,
"variance" : 3.11628,
"median" : 4.35,
"population" : 150,
"minimum" : 1,
"standard_deviation" : 1.7653,
"kurtosis" : -1.39554,
"sum" : 563.7
},
"datatype" : "double",
"order" : 2,
"optype" : "numeric",
"name" : "petal length",
"column_number" : 2
}, {
"id" : "000003",
"preferred" : true,
"summary" : {
"skewness" : -0.10193,
"counts" : [ [ 0.1, 5 ], [ 0.2, 29 ], [ 0.3, 7 ], [ 0.4, 7 ], [ 0.5, 1 ], [ 0.6, 1 ], [ 1, 7 ], [ 1.1, 3 ], [ 1.2, 5 ], [ 1.3, 13 ], [ 1.4, 8 ], [ 1.5, 12 ], [ 1.6, 4 ], [ 1.7, 2 ], [ 1.8, 12 ], [ 1.9, 5 ], [ 2, 6 ], [ 2.1, 6 ], [ 2.2, 3 ], [ 2.3, 8 ], [ 2.4, 3 ], [ 2.5, 3 ] ],
"mean" : 1.19933,
"sum_squares" : 302.33,
"maximum" : 2.5,
"missing_count" : 0,
"variance" : 0.58101,
"median" : 1.3,
"population" : 150,
"minimum" : 0.1,
"standard_deviation" : 0.76224,
"kurtosis" : -1.33607,
"sum" : 179.9
},
"datatype" : "double",
"order" : 3,
"optype" : "numeric",
"name" : "petal width",
"column_number" : 3
}, {
"id" : "000004",
"preferred" : true,
"summary" : {
"missing_count" : 0,
"categories" : [ [ "Iris-setosa", 50 ], [ "Iris-versicolor", 50 ], [ "Iris-virginica", 50 ] ]
},
"datatype" : "string",
"order" : 4,
"term_analysis" : {
"enabled" : true
},
"optype" : "categorical",
"name" : "species",
"column_number" : 4
} ],
"input_fields" : [ "000000", "000001", "000002", "000003", "000004" ],
"missing_numeric_count" : 0,
"datasink" : "file://localhost/tmp/wintermute-tests.cheesinglee/regression/datasink",
"datasource_id" : "1426541387842",
"serialized_bytes" : 6150,
"count" : 150,
"objective" : {
"id" : "000004",
"datatype" : "string",
"order" : 4,
"term_analysis" : {
"enabled" : true
},
"optype" : "categorical",
"name" : "species",
"column_number" : 4
},
"used_bytes" : 4609
}
}
var data = {"fields":[{"maximum":7.9,"minimum":4.3,"missings":false,"datatype":"double","optype":"numeric","id":"000000"},{"maximum":4.4,"minimum":2.0,"missings":false,"datatype":"double","optype":"numeric","id":"000001"},{"maximum":6.9,"minimum":1.0,"missings":false,"datatype":"double","optype":"numeric","id":"000002"},{"maximum":2.5,"minimum":0.1,"missings":false,"datatype":"double","optype":"numeric","id":"000003"},{"categories":["Iris-setosa","Iris-versicolor","Iris-virginica"],"missings":false,"datatype":"byte","optype":"categorical","id":"000004"}],"data":[[5.1,4.9,4.7,4.6,5.0,5.4,4.6,5.0,4.4,4.9,5.4,4.8,4.8,4.3,5.8,5.7,5.4,5.1,5.7,5.1,5.4,5.1,4.6,5.1,4.8,5.0,5.0,5.2,5.2,4.7,4.8,5.4,5.2,5.5,4.9,5.0,5.5,4.9,4.4,5.1,5.0,4.5,4.4,5.0,5.1,4.8,5.1,4.6,5.3,5.0,7.0,6.4,6.9,5.5,6.5,5.7,6.3,4.9,6.6,5.2,5.0,5.9,6.0,6.1,5.6,6.7,5.6,5.8,6.2,5.6,5.9,6.1,6.3,6.1,6.4,6.6,6.8,6.7,6.0,5.7,5.5,5.5,5.8,6.0,5.4,6.0,6.7,6.3,5.6,5.5,5.5,6.1,5.8,5.0,5.6,5.7,5.7,6.2,5.1,5.7,6.3,5.8,7.1,6.3,6.5,7.6,4.9,7.3,6.7,7.2,6.5,6.4,6.8,5.7,5.8,6.4,6.5,7.7,7.7,6.0,6.9,5.6,7.7,6.3,6.7,7.2,6.2,6.1,6.4,7.2,7.4,7.9,6.4,6.3,6.1,7.7,6.3,6.4,6.0,6.9,6.7,6.9,5.8,6.8,6.7,6.7,6.3,6.5,6.2,5.9],[3.5,3.0,3.2,3.1,3.6,3.9,3.4,3.4,2.9,3.1,3.7,3.4,3.0,3.0,4.0,4.4,3.9,3.5,3.8,3.8,3.4,3.7,3.6,3.3,3.4,3.0,3.4,3.5,3.4,3.2,3.1,3.4,4.1,4.2,3.1,3.2,3.5,3.6,3.0,3.4,3.5,2.3,3.2,3.5,3.8,3.0,3.8,3.2,3.7,3.3,3.2,3.2,3.1,2.3,2.8,2.8,3.3,2.4,2.9,2.7,2.0,3.0,2.2,2.9,2.9,3.1,3.0,2.7,2.2,2.5,3.2,2.8,2.5,2.8,2.9,3.0,2.8,3.0,2.9,2.6,2.4,2.4,2.7,2.7,3.0,3.4,3.1,2.3,3.0,2.5,2.6,3.0,2.6,2.3,2.7,3.0,2.9,2.9,2.5,2.8,3.3,2.7,3.0,2.9,3.0,3.0,2.5,2.9,2.5,3.6,3.2,2.7,3.0,2.5,2.8,3.2,3.0,3.8,2.6,2.2,3.2,2.8,2.8,2.7,3.3,3.2,2.8,3.0,2.8,3.0,2.8,3.8,2.8,2.8,2.6,3.0,3.4,3.1,3.0,3.1,3.1,3.1,2.7,3.2,3.3,3.0,2.5,3.0,3.4,3.0],[1.4,1.4,1.3,1.5,1.4,1.7,1.4,1.5,1.4,1.5,1.5,1.6,1.4,1.1,1.2,1.5,1.3,1.4,1.7,1.5,1.7,1.5,1.0,1.7,1.9,1.6,1.6,1.5,1.4,1.6,1.6,1.5,1.5,1.4,1.5,1.2,1.3,1.4,1.3,1.5,1.3,1.3,1.3,1.6,1.9,1.4,1.6,1.4,1.5,1.4,4.7,4.5,4.9,4.0,4.6,4.5,4.7,3.3,4.6,3.9,3.5,4.2,4.0,4.7,3.6,4.4,4.5,4.1,4.5,3.9,4.8,4.0,4.9,4.7,4.3,4.4,4.8,5.0,4.5,3.5,3.8,3.7,3.9,5.1,4.5,4.5,4.7,4.4,4.1,4.0,4.4,4.6,4.0,3.3,4.2,4.2,4.2,4.3,3.0,4.1,6.0,5.1,5.9,5.6,5.8,6.6,4.5,6.3,5.8,6.1,5.1,5.3,5.5,5.0,5.1,5.3,5.5,6.7,6.9,5.0,5.7,4.9,6.7,4.9,5.7,6.0,4.8,4.9,5.6,5.8,6.1,6.4,5.6,5.1,5.6,6.1,5.6,5.5,4.8,5.4,5.6,5.1,5.1,5.9,5.7,5.2,5.0,5.2,5.4,5.1],[0.2,0.2,0.2,0.2,0.2,0.4,0.3,0.2,0.2,0.1,0.2,0.2,0.1,0.1,0.2,0.4,0.4,0.3,0.3,0.3,0.2,0.4,0.2,0.5,0.2,0.2,0.4,0.2,0.2,0.2,0.2,0.4,0.1,0.2,0.2,0.2,0.2,0.1,0.2,0.2,0.3,0.3,0.2,0.6,0.4,0.3,0.2,0.2,0.2,0.2,1.4,1.5,1.5,1.3,1.5,1.3,1.6,1.0,1.3,1.4,1.0,1.5,1.0,1.4,1.3,1.4,1.5,1.0,1.5,1.1,1.8,1.3,1.5,1.2,1.3,1.4,1.4,1.7,1.5,1.0,1.1,1.0,1.2,1.6,1.5,1.6,1.5,1.3,1.3,1.3,1.2,1.4,1.2,1.0,1.3,1.2,1.3,1.3,1.1,1.3,2.5,1.9,2.1,1.8,2.2,2.1,1.7,1.8,1.8,2.5,2.0,1.9,2.1,2.0,2.4,2.3,1.8,2.2,2.3,1.5,2.3,2.0,2.0,1.8,2.1,1.8,1.8,1.8,2.1,1.6,1.9,2.0,2.2,1.5,1.4,2.3,2.4,1.8,1.8,2.1,2.4,2.3,1.9,2.3,2.5,2.3,1.9,2.0,2.3,1.8],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]]};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment