Created
October 1, 2014 13:25
-
-
Save maggie-lee/4247b667f372453789be to your computer and use it in GitHub Desktop.
Multi-series bar graph with clickable legend tutorial
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
State | Pre-teens | Teens | Seniors | |
---|---|---|---|---|
Alabama | 75 | 43.8 | 38.1 | |
Georgia | 80 | 37.4 | 32.7 | |
Louisiana | 82 | 30 | 38.8 | |
Mississippi | 78 | 39.2 | 32.6 | |
South Carolina | 90 | 42.7 | 36 | |
United States | 76 | 41.5 | 35.7 |
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> | |
<meta charset="utf-8"> | |
<title>Cootie Vaccination Rate</title> | |
<head></head> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
font-size: 12px; | |
} | |
.axis path{ | |
stroke: none; | |
fill: none; | |
stroke: #FFF; | |
shape-rendering: crispEdges; | |
} | |
.axis line { | |
stroke: none; | |
fill: none; | |
stroke: #FFF; | |
shape-rendering: crispEdges; | |
} | |
</style> | |
<body> | |
<div class="title"><font size=4> Annual Cootie Vaccination Rate, 2013-2014</font></div> | |
<div class="subtitle">Deep South states v. national average<br> | |
Teens & seniors woefully underprotected from 2014 strain of Cooties<br><br></div> | |
<div class="explainer"> | |
A multi-series bar chart with clickable legend. | |
<br>Based on <a href="http://bost.ocks.org/mike/constancy/" target=_blank>Mike Bostock's Object Constancy example.</a> | |
<br><br> | |
</div> | |
<p>Click to see how your demographic stacks up:</p> | |
<!-- This <div> will hold an svg that holds the legend only. --> | |
<div class = "legendHolder"> </div> | |
<!-- This <div> will hold the drawing --> | |
<div class="chart"></div> | |
<script>document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')</script> | |
<script src="http://d3js.org/d3.v2.min.js?2.9.1"></script> | |
<script> | |
console.log("ok"); | |
// ************ DECLARATIONS | |
var margin = {top: 20, right: 100, bottom: 10, left: 10}, | |
width = 400 | |
height = 150 - margin.top - margin.bottom; | |
var states, | |
age; | |
var x = d3.scale.linear() | |
.domain([0, 100])// Don't use max function. Hardcode 100 b/c 100 is the real-life maximum vaccination rate. | |
.range([0, width]); | |
var y = d3.scale.ordinal() | |
.rangeRoundBands([0, height], .1); | |
var xAxis = d3.svg.axis() | |
.scale(x) | |
.orient("top") | |
.tickSize(-height - margin.bottom); // draws vertical white gridlines | |
// ********* NB, no y Axis! | |
// We won't need it because what looks to the eye like y-axis labels are actually labels stuck to each bar. | |
// ************ | |
var color = d3.scale.category10(); | |
var svg = d3.select(".chart").append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.style("margin-left", -margin.left + "px") | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
svg.append("g") | |
.attr("class", "x axis"); | |
// ******** END DECLARATIONS | |
d3.csv("flu_vaccination.csv", function(data) { | |
// make a copy of data | |
states = data; | |
// ******** | |
//figure out your categories, the things that correspond to column names. | |
// State, Pre-Teens, Teens, Seniors | |
// Alabama, 52.1, 43.8, 38.1 | |
// etc | |
// | |
// Go through the first "row" and make an object that contains only those cells //that are not "State" | |
var ages = d3.keys(states[0]).filter(function(key) { | |
return key != "State"; | |
}); | |
// returns ["Pre-teens", "Teens", "Seniors"] | |
// ************* | |
color.domain(ages); // feed the three ages into the color funciton. Each age will get mapped to a color. | |
default_age = "Pre-teens"; // initialize the age group to be graphed | |
// ******* DATA SORTED, LOADED, FILTERED | |
//******** LEGEND | |
// legendolder is going to be an svg of this hight | |
var legendHolder = d3.select(".legendHolder").append("svg").attr("height", height*0.13); | |
// We're going to append some legend entries into legendHolder | |
var legend = legendHolder.selectAll(".legend") | |
.data(color.domain().slice()) | |
.enter().append("g") | |
.attr("class", "legend") | |
.attr("transform", | |
function (d, i) | |
{ return "translate(" + i*(width*0.2) + ",0)";}); | |
// translate along x: set each legend element at (i * two-tenths of the width) | |
// and dont translate along y at all | |
// ****** make all of the legends's rects and texts clickable. | |
legend.append("rect") | |
.attr("width", 18) | |
.attr("height", 18) | |
.style("fill", color) | |
.on("click", function (d) { | |
default_age = d; // what ever you've clicked on becomes the default_age | |
return change();}); //then default_age will be ready when redraw() calls it | |
legend.append("text") | |
.attr("dy", "1em") | |
.attr("dx", width*0.05) | |
.style("text-anchor", "start") | |
.text(function (d) {return d; }) | |
.on("click", function (d) { | |
default_age = d; // what ever you've clicked on becomes the default_age | |
return change();}); //then default_age will be ready when redraw() calls it | |
// ************** | |
redraw(); | |
}); // CLOSE INITIAL FUNCTION | |
// ******** CHANGE FUNCTION | |
function change() { // defines very simple transition. redraw() each bar and take 750 ms to do it. | |
d3.transition() | |
.duration(750) | |
.each(redraw); | |
} | |
//********* DRAW FUNCTION | |
function redraw() { | |
// declare a local variable to hold default_age | |
var age1 = default_age, | |
// then sort by this local variable age1 | |
top = states.sort(function(a, b) { return b[age1] - a[age1]; }); | |
// Map y.domain to the states in the newly-sorted data | |
y.domain(top.map(function(d) { return d.State; })); | |
// Using the sorted data, make a bar for each state and class it ".bar" | |
var bar = svg.selectAll(".bar") | |
.data(top, function(d) { return d.State; }) | |
.attr("class", "bar"); | |
// entering transition. This can be and is very bare-bones. Just "hey, make some bars and attach the state names to each one" | |
var barEnter = bar.enter().insert("g", ".axis") | |
.attr("class", "bar"); | |
barEnter.append("rect") | |
.attr("height", y.rangeBand()); | |
barEnter.append("text") // Attach the state name to the bar. So the state name always travels with it | |
.attr("class", "label") | |
.attr("x", -3) | |
.attr("y", y.rangeBand() / 2) | |
.attr("dy", ".35em") | |
.attr("text-anchor", "end") | |
.text(function (d) {return d.State;}); | |
//now drawing updated bar | |
age = age1; // put the age that's been clicked on into global variable age | |
// d3.transition(bar) is where the magic is. Makes each bar smoothly move where it needs to be | |
var barUpdate = d3.transition(bar) | |
//transform bars a little to the right b/c we have long labels | |
.attr("transform", function(d) { return "translate(80," + (d.y0 = y(d.State)) + ")"; }); | |
barUpdate.select("rect") | |
.attr("width", function (d) { return x(d[age]);}) | |
.attr("fill", function (d) {return color(age) ;}) | |
.attr("fill-opacity", function (d) { | |
if (d.State == "United States") {return 0.5;} ;} );// Highlight U.S. average | |
// now to wipe away bars | |
// pretty much we are just going to remove() them. | |
var barExit = d3.transition(bar.exit()) | |
.remove(); | |
barExit.select("rect") | |
d3.transition(svg).select(".axis") | |
//again, translate a little further right for space for labels | |
.attr("transform", function(d) { return "translate(80," + 0 + ")"; }) | |
.call(xAxis); | |
} | |
// ****** END DRAW FUNCTION | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment