Created
September 17, 2012 18:23
-
-
Save madelfio/3738896 to your computer and use it in GitHub Desktop.
Logarithmic Binning
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
var x_range = [1, 32], | |
y_range = [0, x_range[1]]; | |
var margin = {top: 30, bottom: 60, left:110, right: 10}, | |
base_width = 960, | |
base_height = 470, | |
width = base_width - margin.left - margin.right, | |
height = base_height - margin.top - margin.bottom; | |
var x = d3.scale.log() | |
.domain(x_range) | |
.range([margin.left, margin.left + width]); | |
var y = d3.scale.linear() | |
.domain(y_range) | |
.range([height + margin.top, margin.top]); | |
var y2 = d3.scale.linear() | |
.domain([0,1]) | |
.range([height + margin.top, margin.top]); | |
var x_axis = d3.svg.axis() | |
.scale(x) | |
.tickValues([1,2,3,4,5,6,7,8,10,12,14,16,18,20,22,24,26,28,30]) | |
.tickFormat(d3.format(',.0f')) | |
.orient('bottom'); | |
var y_axis = d3.svg.axis() | |
.scale(y) | |
.tickValues([0,1,2,4,8,16]) | |
.tickFormat(tickFmtStandard) | |
.tickSize(-width,0,6) | |
.orient('left'); | |
var palette = ["#393b79", "#b5cf6b", "#843c39", "#de9ed6", "#8ca252", | |
"#bd9e39", "#aecb8c", "#8c6d31", "#e7ba52", "#e7cb94", | |
"#6b6ecf", "#ce6dbd", "#ad494a", "#5254a3", "#637939", | |
"#d6616b", "#7b4173", "#e7969c", "#a55194", "#9c9ede"]; | |
var color = d3.scale.ordinal().range(palette); | |
var data = []; | |
for (var i = x_range[0]; i < x_range[1]; i++) { | |
for (var j = y_range[0]; j <= i; j++) { | |
data.push([i, j]); | |
} | |
} | |
var data_borders = []; | |
data.forEach(function(v) { | |
data_borders.push([i,j,'l']); | |
data_borders.push([i,j,'t']); | |
data_borders.push([i,j,'r']); | |
data_borders.push([i,j,'b']); | |
}); | |
function getBin(x, y) { | |
var b = Math.floor(Math.log(x)/Math.log(2)); | |
var a = 0; | |
if (y === 0) { | |
a = 0; | |
} else if (y <= x/2) { | |
a = Math.floor(Math.log(y)/Math.log(2) + 1); | |
} else if (y < x) { | |
a = -Math.floor(Math.log(x - y)/Math.log(2) + 1); | |
} else { | |
a = -0.1; | |
} | |
return [a, b]; | |
//return '(' + a + ', ' + b + ')'; | |
} | |
function getBinStr(x, y) { | |
var bin = getBin(x, y); | |
return '(' + bin[0] + ', ' + bin[1] + ')'; | |
} | |
function getBinCls(x, y) { | |
var bin = getBin(x, y); | |
return 'B' + (bin[0] * 10) + 'X' + bin[1]; | |
} | |
var chart = d3.select('#binning') | |
.append('svg') | |
.attr('width', base_width) | |
.attr('height', base_height); | |
chart.append('svg:rect') | |
.attr('x', 0) | |
.attr('y', 0) | |
.attr('width', base_width) | |
.attr('height', base_height) | |
.attr('fill', '#ddd'); | |
var gx = chart.append('g') | |
.attr('class', 'x axis') | |
.attr('transform', 'translate(0,' + y(0) + ')') | |
.call(x_axis); | |
gx.selectAll('text') | |
.attr('x', function(d) {return (x(d + 1) - x(d)) / 2;}); | |
var axis_title_x = (x(x_range[1]) + x(x_range[0])) / 2; | |
gx.append('g').append('text') | |
.attr('x', function() {return axis_title_x;}) | |
.attr('y', function() {return 40;}) | |
.attr('text-anchor', 'middle') | |
.text('r (number of cells in row)'); | |
var gy_base = chart.append('g') | |
.attr('transform', 'translate(' + x(1) + ',0)'); | |
var gy = gy_base.append('g') | |
.attr('class', 'y axis') | |
.call(y_axis); | |
gy.selectAll('text') | |
.attr('y', function(d) {return (y(d + 1) - y(d)) / 2;}); | |
gy_base.append('g') | |
.attr('transform', 'translate(-70,' + ((y(y_range[1]) + y(y_range[0])) / 2) + ')') | |
.append('text') | |
.attr('text-anchor', 'middle') | |
.attr('transform', 'rotate(-90)') | |
.text('c (number of cells that share a given attribute)'); | |
gy.selectAll('.domain') | |
.attr('fill', 'none') | |
.attr('stroke', 'black') | |
.attr('stroke-width', 1); | |
var bin = chart.selectAll('rect.bin').data(data) | |
.enter().append('svg:rect') | |
.attr('x', function(d, index) {return x(d[0]);}) | |
.attr('y', function(d, index) {return y(d[1] + 1);}) | |
.attr('width', function(d, index) {return x(d[0]+1) - x(d[0]) - 1;}) | |
.attr('height', function(d, index) {return - y(d[1] + 1) + y(d[1]) - 1;}) | |
.attr('class', function(d) {return getBinCls(d[0], d[1]);}) | |
.classed('bin', true) | |
.attr('fill', function(d, index) {return color(getBinStr(d[0], d[1]));}) //'#bfb') | |
.on('mouseover', highlightBin) | |
.on('mouseout', unhighlightBin) | |
.append('svg:title') | |
.text(function(d) {return 'c = '+d[1]+', r = ' + d[0];}); | |
function highlightBin(d, i) { | |
var my_bin = getBinCls(d[0], d[1]); | |
var new_color = d3.rgb(color(getBinStr(d[0], d[1]))).brighter(); | |
chart.selectAll('rect.' + my_bin) | |
.attr('stroke', 'black') | |
.attr('stroke-width', 1) | |
.attr('fill', new_color); | |
} | |
function unhighlightBin(d, i) { | |
var my_bin = getBinCls(d[0], d[1]); | |
var orig_color = d3.rgb(color(getBinStr(d[0], d[1]))); | |
chart.selectAll('rect.' + my_bin) | |
.attr('stroke', 'none') | |
.attr('fill', orig_color); | |
} | |
d3.select('#standard-btn').on('click', function() { | |
chart.selectAll('rect.bin') | |
.transition() | |
.duration(1000) | |
.attr('y', function(d, index) {return y(d[1] + 1);}) | |
.attr('height', function(d, index) {return - y(d[1] + 1) + y(d[1]) - 1;}); | |
y_axis | |
.tickValues([0,1,2,4,8,16]) | |
.tickFormat(tickFmtStandard); | |
gy.call(y_axis); | |
gy.selectAll('text') | |
.attr('y', function(d) {return (y(d + 1) - y(d)) / 2;}); | |
gy.selectAll('line') | |
.attr('y1', null) | |
.attr('y2', null) | |
.attr('display', null); | |
}); | |
d3.select('#symmetric-btn').on('click', function() { | |
chart.selectAll('rect.bin') | |
.transition() | |
.duration(1000) | |
.attr('y', function(d, index) {return y((d[1] <= d[0] / 2 ? d[1] + 1: x_range[1] - (d[0] - d[1])));}) | |
.attr('height', function(d, index) {return - y(d[1] + 1) + y(d[1]) - 1;}); | |
y_axis | |
.tickValues([0,1,2,4,8,15,16,23,27,29,30,31]) | |
.tickFormat(tickFmtSymmetric); | |
gy.call(y_axis); | |
gy.selectAll('text') | |
.attr('y', function(d) {return (y(d + 1) - y(d)) / 2;}); | |
gy.selectAll('line') | |
.attr('y1', function(d) {return (d >= y_range[1]/2 - 1) ? y(d+1)-y(d) : 0}) | |
.attr('y2', function(d) {return (d >= y_range[1]/2 - 1) ? y(d+1)-y(d) : 0}) | |
.attr('display', function(d) {return d === y_range[1]/2 ? 'none' : null}); | |
}); | |
d3.select('#stretched-btn').on('click', function() { | |
chart.selectAll('rect.bin') | |
.transition() | |
.duration(1000) | |
.attr('y', function(d, index) {return y2((d[1]+1)/(d[0]+1));}) | |
.attr('height', function(d, index) {return y2(d[1]/(d[0]+1)) - y2((d[1]+1)/(d[0]+1)) - 1;}); | |
y_axis | |
.tickValues([0,15,16,31]) | |
.tickFormat(tickFmtSymmetric); | |
gy.call(y_axis) | |
.selectAll('text') | |
.attr('y', function(d) {return (y(d+1) - y(d)) / 2;}); | |
gy.selectAll('line') | |
.attr('y1', function(d) {return (d >= y_range[1]/2 - 1) ? y(d+1)-y(d) : 0}) | |
.attr('y2', function(d) {return (d >= y_range[1]/2 - 1) ? y(d+1)-y(d) : 0}) | |
.attr('display', function(d) {return d === y_range[1]/2 ? 'none' : null}); | |
}); | |
function tickFmtStandard(v) { | |
if (v === y_range[0] || v === y_range[1] - 1) { | |
return 'c = ' + v; | |
} else { | |
return '' + v; | |
} | |
} | |
function tickFmtSymmetric(v) { | |
if (v === y_range[0]) { | |
return 'c = ' + v; | |
} else if (v === y_range[1] - 1) { | |
return 'c = r'; | |
} else if (v === (y_range[1] / 2)) { | |
return 'c > r/2'; | |
} else if (v === (y_range[1] / 2 - 1)) { | |
return 'c ≤ r/2'; | |
} else if (v > (y_range[1] / 2 - 1)) { | |
return 'r - ' + (y_range[1] - 1 - v); | |
} else { | |
return '' + v; | |
} | |
} | |
function tickFmtStretched(v) { | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<head> | |
<meta charset="utf-8"> | |
<!--link href="./style.css" rel="stylesheet" /--> | |
<style> | |
.axis line {stroke:#666;stroke-opacity:0.2;stroke-dasharray: 9, 5;} | |
</style> | |
</head> | |
<body> | |
<div id="binning"> | |
Bins used in the logarithmic binning method.<br /> | |
Layout: <button id="standard-btn">Standard</button><button id="symmetric-btn">Symmetric</button><button id="stretched-btn">Stretched</button><br /> | |
</div> | |
<script src="http://d3js.org/d3.v2.min.js"></script> | |
<script src="./binning.js"></script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment