Last active
October 19, 2015 13:28
-
-
Save nitaku/32893c305aa87a65129a to your computer and use it in GitHub Desktop.
Multivariate binning II
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
# set the frame size | |
d3.select(self.frameElement).style('height', '1440px') | |
side = 480 | |
# DATA | |
distributions = [ | |
d3.range(1500).map( () -> {x: d3.random.normal(side/2, 80)(), y: d3.random.normal(side/2, 80)()} ), | |
d3.range(1500).map( () -> {x: d3.random.normal(side/2, 80)() - 50, y: 100+d3.random.normal(side/2, 80)()} ), | |
d3.range(1500).map( () -> {x: d3.random.normal(side/2, 80)(), y: d3.random.normal(side/2, 80)() - 100} ), | |
d3.range(1000).map( () -> {x: d3.random.normal(3.5*side/4, 80)(), y: d3.random.normal(side/2, 80)()} ) | |
] | |
classes = distributions.length | |
points = _.chain(distributions) | |
.map( (distribution, klass) -> | |
distribution.forEach (point) -> point.class = klass | |
return distribution | |
) | |
.flatten(true) | |
.value() | |
# hexagonal binning | |
radius = 18 | |
apothem = Math.sqrt(3)/2 * radius | |
hexbin = d3.hexbin() | |
.size([side, side]) | |
.radius(radius) | |
.x((d) -> d.x) | |
.y((d) -> d.y) | |
bins = _.chain(hexbin(points)) | |
.forEach( (bin) -> | |
bin.classes_count = _.chain(_.range(classes)) | |
.map( (klass) -> bin.filter( (point) -> point.class is klass ).length ) | |
.value() | |
) | |
.value() | |
max_tot = d3.max(bins, (bin) -> bin.length) | |
max = d3.max(bins, (bin) -> d3.max(bin.classes_count) ) | |
angle = 2*Math.PI / classes | |
svg = d3.select('svg') | |
upper_left = svg.append('g') | |
.attr('id', 'dots') | |
.attr('clip-path', 'url(#square_window)') | |
upper_right = svg.append('g') | |
.attr('id', 'bars') | |
.attr('clip-path', 'url(#square_window)') | |
.attr('transform', "translate(#{side},0)") | |
middle_left = svg.append('g') | |
.attr('id', 'pies') | |
.attr('clip-path', 'url(#square_window)') | |
.attr('transform', "translate(0,#{side})") | |
middle_right = svg.append('g') | |
.attr('id', 'arcs') | |
.attr('clip-path', 'url(#square_window)') | |
.attr('transform', "translate(#{side},#{side})") | |
bottom_left = svg.append('g') | |
.attr('id', 'polar_length') | |
.attr('clip-path', 'url(#square_window)') | |
.attr('transform', "translate(0,#{2*side})") | |
bottom_right = svg.append('g') | |
.attr('id', 'polar_area') | |
.attr('clip-path', 'url(#square_window)') | |
.attr('transform', "translate(#{side},#{2*side})") | |
svg.append('line') | |
.attr | |
class: 'separator' | |
x1: 0 | |
x2: 2*side | |
y1: side | |
y2: side | |
svg.append('line') | |
.attr | |
class: 'separator' | |
x1: 0 | |
x2: 2*side | |
y1: 2*side | |
y2: 2*side | |
svg.append('line') | |
.attr | |
class: 'separator' | |
x1: side | |
x2: side | |
y1: 0 | |
y2: 3*side | |
#color = d3.scale.ordinal() | |
# .domain([0, classes]) | |
# .range(["#1b9e77","#d95f02","#66a61e","#e6ab02","#e7298a","#a6761d","#666666"]) | |
color = d3.scale.category10() | |
# dot density plot | |
dots = d3.select('#dots').selectAll('.dot') | |
.data(points) | |
dots.enter().append('circle') | |
.attr | |
class: 'dot' | |
r: 1 | |
cx: (p) -> p.x | |
cy: (p) -> p.y | |
fill: (d) -> color(d.class) | |
# bar chart subplotting | |
subplots = d3.select('#bars').selectAll('.subplot') | |
.data(bins) | |
subplots.enter().append('g') | |
.attr | |
class: 'subplot' | |
transform: (bin) -> "translate(#{bin.x},#{bin.y})" | |
y_scale = d3.scale.linear() | |
.domain([0, max]) | |
.range([0, radius]) | |
x_scale = d3.scale.ordinal() | |
.domain(_.range(classes)) | |
.rangeRoundBands([-radius/2, radius/2], 0.05) | |
bars = subplots.selectAll('.bar') | |
.data((bin) -> bin.classes_count) | |
bars.enter().append('rect') | |
.attr | |
class: 'bar' | |
x: (count, klass) -> x_scale(klass) | |
y: (count) -> y_scale(max-count) | |
width: x_scale.rangeBand() | |
height: (count) -> y_scale(count) | |
fill: (count, klass) -> color(klass) | |
transform: "translate(0,#{-radius})" | |
# pie chart subplotting | |
subplots = d3.select('#pies').selectAll('.subplot') | |
.data(bins) | |
subplots.enter().append('g') | |
.attr | |
class: 'subplot' | |
transform: (bin) -> "translate(#{bin.x},#{bin.y})" | |
pie_layout = d3.layout.pie() | |
.sort(null) | |
radius_scale = d3.scale.sqrt() | |
.domain([0, max_tot]) | |
.range([0, apothem]) | |
arc_generator = d3.svg.arc() | |
.outerRadius((count) -> radius_scale( d3.select(this.parentNode).datum().length )) | |
.innerRadius(0) | |
pies = subplots.selectAll('.pie') | |
.data((bin) -> pie_layout(bin.classes_count)) | |
pies.enter().append('path') | |
.attr | |
class: 'pie' | |
d: arc_generator | |
fill: (count, klass) -> color(klass) | |
# arc chart subplotting | |
subplots = d3.select('#arcs').selectAll('.subplot') | |
.data(bins) | |
subplots.enter().append('g') | |
.attr | |
class: 'subplot' | |
transform: (bin) -> "translate(#{bin.x},#{bin.y})" | |
radius_scale = d3.scale.linear() | |
.domain([0, max]) | |
.range([0, apothem*0.9]) | |
inner_radius_scale = d3.scale.linear() | |
.domain([0, max]) | |
.range([apothem*0.4, apothem*0.9]) | |
arc_generator = d3.svg.arc() | |
.innerRadius((count) -> inner_radius_scale(max-count)) | |
.outerRadius((count) -> radius_scale(max)) | |
.startAngle((count, klass) -> klass*angle - angle/2) | |
.endAngle((count, klass) -> klass*angle + angle/2) | |
pies = subplots.selectAll('.pie') | |
.data((bin) -> bin.classes_count) | |
pies.enter().append('path') | |
.attr | |
class: 'pie' | |
d: arc_generator | |
fill: (count, klass) -> color(klass) | |
# polar length chart subplotting | |
subplots = d3.select('#polar_length').selectAll('.subplot') | |
.data(bins) | |
subplots.enter().append('g') | |
.attr | |
class: 'subplot' | |
transform: (bin) -> "translate(#{bin.x},#{bin.y})" | |
radius_scale = d3.scale.linear() | |
.domain([0, max]) | |
.range([0, apothem]) | |
arc_generator = d3.svg.arc() | |
.innerRadius(0) | |
.outerRadius((count) -> radius_scale(count)) | |
.startAngle((count, klass) -> klass*angle - angle/2) | |
.endAngle((count, klass) -> klass*angle + angle/2) | |
pies = subplots.selectAll('.pie') | |
.data((bin) -> bin.classes_count) | |
pies.enter().append('path') | |
.attr | |
class: 'pie' | |
d: arc_generator | |
fill: (count, klass) -> color(klass) | |
# polar area chart subplotting | |
subplots = d3.select('#polar_area').selectAll('.subplot') | |
.data(bins) | |
subplots.enter().append('g') | |
.attr | |
class: 'subplot' | |
transform: (bin) -> "translate(#{bin.x},#{bin.y})" | |
radius_scale = d3.scale.sqrt() | |
.domain([0, max]) | |
.range([0, apothem]) | |
arc_generator = d3.svg.arc() | |
.innerRadius(0) | |
.outerRadius((count) -> radius_scale(count)) | |
.startAngle((count, klass) -> klass*angle - angle/2) | |
.endAngle((count, klass) -> klass*angle + angle/2) | |
pies = subplots.selectAll('.pie') | |
.data((bin) -> bin.classes_count) | |
pies.enter().append('path') | |
.attr | |
class: 'pie' | |
d: arc_generator | |
fill: (count, klass) -> color(klass) |
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
svg { | |
background-color: white; | |
} | |
.separator { | |
stroke: #DEDEDE; | |
fill: none; | |
shape-rendering: crispEdges; | |
} | |
.outer_polygon { | |
fill: #EEE; | |
} | |
.pie { | |
stroke: white; | |
stroke-width: 0.5; | |
} | |
#pies .pie { | |
stroke: none; | |
} |
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> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="description" content="Multivariate binning II" /> | |
<title>Multivariate binning II</title> | |
<link rel="stylesheet" href="index.css"> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="http://d3js.org/d3.hexbin.v0.min.js?5c6e4f0"></script> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script> | |
</head> | |
<body> | |
<svg height="1440" width="960"> | |
<defs> | |
<clipPath id="square_window"> | |
<rect x="0" y="0" width="480.5" height="480.5" /> | |
</clipPath> | |
</defs> | |
</svg> | |
<script src="index.js"></script> | |
</body> | |
</html> |
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
(function() { | |
var angle, apothem, arc_generator, bars, bins, bottom_left, bottom_right, classes, color, distributions, dots, hexbin, inner_radius_scale, max, max_tot, middle_left, middle_right, pie_layout, pies, points, radius, radius_scale, side, subplots, svg, upper_left, upper_right, x_scale, y_scale; | |
d3.select(self.frameElement).style('height', '1440px'); | |
side = 480; | |
distributions = [ | |
d3.range(1500).map(function() { | |
return { | |
x: d3.random.normal(side / 2, 80)(), | |
y: d3.random.normal(side / 2, 80)() | |
}; | |
}), d3.range(1500).map(function() { | |
return { | |
x: d3.random.normal(side / 2, 80)() - 50, | |
y: 100 + d3.random.normal(side / 2, 80)() | |
}; | |
}), d3.range(1500).map(function() { | |
return { | |
x: d3.random.normal(side / 2, 80)(), | |
y: d3.random.normal(side / 2, 80)() - 100 | |
}; | |
}), d3.range(1000).map(function() { | |
return { | |
x: d3.random.normal(3.5 * side / 4, 80)(), | |
y: d3.random.normal(side / 2, 80)() | |
}; | |
}) | |
]; | |
classes = distributions.length; | |
points = _.chain(distributions).map(function(distribution, klass) { | |
distribution.forEach(function(point) { | |
return point["class"] = klass; | |
}); | |
return distribution; | |
}).flatten(true).value(); | |
radius = 18; | |
apothem = Math.sqrt(3) / 2 * radius; | |
hexbin = d3.hexbin().size([side, side]).radius(radius).x(function(d) { | |
return d.x; | |
}).y(function(d) { | |
return d.y; | |
}); | |
bins = _.chain(hexbin(points)).forEach(function(bin) { | |
return bin.classes_count = _.chain(_.range(classes)).map(function(klass) { | |
return bin.filter(function(point) { | |
return point["class"] === klass; | |
}).length; | |
}).value(); | |
}).value(); | |
max_tot = d3.max(bins, function(bin) { | |
return bin.length; | |
}); | |
max = d3.max(bins, function(bin) { | |
return d3.max(bin.classes_count); | |
}); | |
angle = 2 * Math.PI / classes; | |
svg = d3.select('svg'); | |
upper_left = svg.append('g').attr('id', 'dots').attr('clip-path', 'url(#square_window)'); | |
upper_right = svg.append('g').attr('id', 'bars').attr('clip-path', 'url(#square_window)').attr('transform', "translate(" + side + ",0)"); | |
middle_left = svg.append('g').attr('id', 'pies').attr('clip-path', 'url(#square_window)').attr('transform', "translate(0," + side + ")"); | |
middle_right = svg.append('g').attr('id', 'arcs').attr('clip-path', 'url(#square_window)').attr('transform', "translate(" + side + "," + side + ")"); | |
bottom_left = svg.append('g').attr('id', 'polar_length').attr('clip-path', 'url(#square_window)').attr('transform', "translate(0," + (2 * side) + ")"); | |
bottom_right = svg.append('g').attr('id', 'polar_area').attr('clip-path', 'url(#square_window)').attr('transform', "translate(" + side + "," + (2 * side) + ")"); | |
svg.append('line').attr({ | |
"class": 'separator', | |
x1: 0, | |
x2: 2 * side, | |
y1: side, | |
y2: side | |
}); | |
svg.append('line').attr({ | |
"class": 'separator', | |
x1: 0, | |
x2: 2 * side, | |
y1: 2 * side, | |
y2: 2 * side | |
}); | |
svg.append('line').attr({ | |
"class": 'separator', | |
x1: side, | |
x2: side, | |
y1: 0, | |
y2: 3 * side | |
}); | |
color = d3.scale.category10(); | |
dots = d3.select('#dots').selectAll('.dot').data(points); | |
dots.enter().append('circle').attr({ | |
"class": 'dot', | |
r: 1, | |
cx: function(p) { | |
return p.x; | |
}, | |
cy: function(p) { | |
return p.y; | |
}, | |
fill: function(d) { | |
return color(d["class"]); | |
} | |
}); | |
subplots = d3.select('#bars').selectAll('.subplot').data(bins); | |
subplots.enter().append('g').attr({ | |
"class": 'subplot', | |
transform: function(bin) { | |
return "translate(" + bin.x + "," + bin.y + ")"; | |
} | |
}); | |
y_scale = d3.scale.linear().domain([0, max]).range([0, radius]); | |
x_scale = d3.scale.ordinal().domain(_.range(classes)).rangeRoundBands([-radius / 2, radius / 2], 0.05); | |
bars = subplots.selectAll('.bar').data(function(bin) { | |
return bin.classes_count; | |
}); | |
bars.enter().append('rect').attr({ | |
"class": 'bar', | |
x: function(count, klass) { | |
return x_scale(klass); | |
}, | |
y: function(count) { | |
return y_scale(max - count); | |
}, | |
width: x_scale.rangeBand(), | |
height: function(count) { | |
return y_scale(count); | |
}, | |
fill: function(count, klass) { | |
return color(klass); | |
}, | |
transform: "translate(0," + (-radius) + ")" | |
}); | |
subplots = d3.select('#pies').selectAll('.subplot').data(bins); | |
subplots.enter().append('g').attr({ | |
"class": 'subplot', | |
transform: function(bin) { | |
return "translate(" + bin.x + "," + bin.y + ")"; | |
} | |
}); | |
pie_layout = d3.layout.pie().sort(null); | |
radius_scale = d3.scale.sqrt().domain([0, max_tot]).range([0, apothem]); | |
arc_generator = d3.svg.arc().outerRadius(function(count) { | |
return radius_scale(d3.select(this.parentNode).datum().length); | |
}).innerRadius(0); | |
pies = subplots.selectAll('.pie').data(function(bin) { | |
return pie_layout(bin.classes_count); | |
}); | |
pies.enter().append('path').attr({ | |
"class": 'pie', | |
d: arc_generator, | |
fill: function(count, klass) { | |
return color(klass); | |
} | |
}); | |
subplots = d3.select('#arcs').selectAll('.subplot').data(bins); | |
subplots.enter().append('g').attr({ | |
"class": 'subplot', | |
transform: function(bin) { | |
return "translate(" + bin.x + "," + bin.y + ")"; | |
} | |
}); | |
radius_scale = d3.scale.linear().domain([0, max]).range([0, apothem * 0.9]); | |
inner_radius_scale = d3.scale.linear().domain([0, max]).range([apothem * 0.4, apothem * 0.9]); | |
arc_generator = d3.svg.arc().innerRadius(function(count) { | |
return inner_radius_scale(max - count); | |
}).outerRadius(function(count) { | |
return radius_scale(max); | |
}).startAngle(function(count, klass) { | |
return klass * angle - angle / 2; | |
}).endAngle(function(count, klass) { | |
return klass * angle + angle / 2; | |
}); | |
pies = subplots.selectAll('.pie').data(function(bin) { | |
return bin.classes_count; | |
}); | |
pies.enter().append('path').attr({ | |
"class": 'pie', | |
d: arc_generator, | |
fill: function(count, klass) { | |
return color(klass); | |
} | |
}); | |
subplots = d3.select('#polar_length').selectAll('.subplot').data(bins); | |
subplots.enter().append('g').attr({ | |
"class": 'subplot', | |
transform: function(bin) { | |
return "translate(" + bin.x + "," + bin.y + ")"; | |
} | |
}); | |
radius_scale = d3.scale.linear().domain([0, max]).range([0, apothem]); | |
arc_generator = d3.svg.arc().innerRadius(0).outerRadius(function(count) { | |
return radius_scale(count); | |
}).startAngle(function(count, klass) { | |
return klass * angle - angle / 2; | |
}).endAngle(function(count, klass) { | |
return klass * angle + angle / 2; | |
}); | |
pies = subplots.selectAll('.pie').data(function(bin) { | |
return bin.classes_count; | |
}); | |
pies.enter().append('path').attr({ | |
"class": 'pie', | |
d: arc_generator, | |
fill: function(count, klass) { | |
return color(klass); | |
} | |
}); | |
subplots = d3.select('#polar_area').selectAll('.subplot').data(bins); | |
subplots.enter().append('g').attr({ | |
"class": 'subplot', | |
transform: function(bin) { | |
return "translate(" + bin.x + "," + bin.y + ")"; | |
} | |
}); | |
radius_scale = d3.scale.sqrt().domain([0, max]).range([0, apothem]); | |
arc_generator = d3.svg.arc().innerRadius(0).outerRadius(function(count) { | |
return radius_scale(count); | |
}).startAngle(function(count, klass) { | |
return klass * angle - angle / 2; | |
}).endAngle(function(count, klass) { | |
return klass * angle + angle / 2; | |
}); | |
pies = subplots.selectAll('.pie').data(function(bin) { | |
return bin.classes_count; | |
}); | |
pies.enter().append('path').attr({ | |
"class": 'pie', | |
d: arc_generator, | |
fill: function(count, klass) { | |
return color(klass); | |
} | |
}); | |
}).call(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment