Skip to content

Instantly share code, notes, and snippets.

@madelfio
Created September 17, 2012 18:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save madelfio/3738896 to your computer and use it in GitHub Desktop.
Save madelfio/3738896 to your computer and use it in GitHub Desktop.
Logarithmic Binning
'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) {
}
<!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