Created
November 2, 2015 16:08
-
-
Save jowang0319/c7d9dfa0e8b8f5088107 to your computer and use it in GitHub Desktop.
Week 10: tooltips
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
Causes | 0-27 days | 1-59 months | 5-14 years | 15-29 years | 30-49 years | 50-59 years | 60-69 years | 70+ years | |
---|---|---|---|---|---|---|---|---|---|
HIV/AIDS | 446 | 44911 | 43579 | 108754 | 313285 | 43264 | 13563 | 4391 | |
Diarrhoealdiseases | 4085 | 147953 | 45706 | 22471 | 23188 | 10032 | 14348 | 29802 | |
Parasiticandvectordiseases | 3 | 228985 | 25280 | 14979 | 14638 | 6294 | 5890 | 10246 | |
Malaria | 0 | 227114 | 14597 | 10483 | 9700 | 3565 | 3552 | 7474 | |
Respiratoryinfections | 28787 | 183134 | 34728 | 27111 | 26234 | 20657 | 36572 | 121805 | |
Lowerrespiratoryinfections | 28748 | 182660 | 34563 | 27033 | 26158 | 20598 | 36490 | 121568 | |
Neonatalconditions | 356160 | 37225 | 0 | 0 | 0 | 0 | 0 | 0 | |
Pretermbirthcomplications | 143914 | 22427 | 0 | 0 | 0 | 0 | 0 | 0 | |
Birthasphyxiaandbirthtrauma | 128327 | 11613 | 0 | 0 | 0 | 0 | 0 | 0 | |
Nutritionaldeficiencies | 0 | 51738 | 27449 | 20168 | 17908 | 9174 | 12787 | 34889 | |
Malignantneoplasms | 0 | 1619 | 3969 | 13772 | 56899 | 50079 | 54241 | 51896 | |
Cardiovasculardiseases | 0 | 5195 | 9865 | 16107 | 42538 | 57462 | 100466 | 281029 | |
Ischaemicheartdisease | 0 | 1130 | 1444 | 3550 | 10596 | 16696 | 31175 | 81729 | |
Stroke | 0 | 0 | 2930 | 4805 | 17727 | 26003 | 45748 | 133261 | |
Digestivediseases | 0 | 6276 | 11198 | 12035 | 20650 | 17950 | 24209 | 41744 | |
Injuries | 5108 | 65640 | 68832 | 60400 | 39524 | 14880 | 15151 | 32537 | |
Unintentionalinjuries | 4088 | 62002 | 61010 | 42539 | 30140 | 12119 | 12736 | 28691 | |
Roadinjury | 244 | 7852 | 14745 | 15326 | 11741 | 4325 | 3830 | 5447 |
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> | |
<!-- code loosely inspired by this block https://gist.github.com/mstanaland/6100713 --> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
font: 12px sans-serif; | |
padding: 50px; | |
} | |
#form { | |
position: relative; | |
right: 10px; | |
top: 10px; | |
padding-bottom: 20px; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
.bar { | |
fill: steelblue; | |
} | |
.myTooltip { | |
position: absolute; | |
z-index: 10; | |
} | |
.myTooltip p { | |
font-family: 'Roboto', sans-serif; | |
background-color: rgba(255, 255, 255, .8); | |
padding: .5em 1em; | |
font-size: 12px; | |
line-height: 17px; | |
color: black; | |
} | |
.tooltipHeader { | |
font-weight: 700; | |
font-size: 12.5px; | |
} | |
svg rect:hover { | |
opacity: .3; | |
} | |
svg circle:hover { | |
opacity: .3; | |
} | |
.unfocused { | |
stroke-opacity: 25%; | |
} | |
.focused { | |
stroke-width: 2px; | |
stroke-opacity: 100%; | |
} | |
.x.axis path { | |
display: none; | |
} | |
</style> | |
<body> | |
<h2>Deaths from Illnesses in 2013 in Selected South African Countries</h2> | |
<p><a href="http://apps.who.int/gho/data/node.main.ghe100-by-cause?lang=en">WHO data</a>, major cause of death of women in Afica Region by age, year 2012.</p> | |
<div id="form"> | |
<label><input type="radio" name="mode" value="bycount" checked>Raw Count</label> | |
<label><input type="radio" name="mode" value="bypercent">Percent of Illness</label> | |
</div> | |
<div id="chart"></div> | |
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script> | |
<script> | |
var margin = {top: 20, right: 150, bottom: 100, left: 55}, | |
width = 1200 - margin.left - margin.right, | |
height = 600 - margin.top - margin.bottom; | |
var xScale = d3.scale.ordinal() | |
.rangeRoundBands([0, width], .3); | |
var yScale = d3.scale.linear() | |
.rangeRound([height, 0]); | |
var color = d3.scale.category20(); | |
var xAxis = d3.svg.axis() | |
.scale(xScale) | |
.orient("bottom") | |
.innerTickSize([0]); | |
var yAxis = d3.svg.axis() | |
.scale(yScale) | |
.orient("left") | |
.tickFormat(d3.format(".2s")); // for the stacked totals version | |
var stack = d3.layout | |
.stack(); | |
var svg = d3.select("#chart").append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
d3.csv("data2.csv", function(error, data) { | |
if (error) { | |
console.log(error); | |
} | |
data.sort(function(a,b) { return a.Causes - b.Causes;}); | |
// how would we sort by largest total bar? what would we have to calculate? | |
var ageRange = ["0-27 days","1-59 months","5-14 years","15-29 years", | |
"30-49 years","50-59 years","60-69 years","70+ years"]; | |
dataToStack = makeData(ageRange, data); | |
console.log(dataToStack); | |
var stacked = stack(dataToStack); | |
console.log(stacked); | |
xScale.domain(data.map(function(d) { return d.Causes; })); | |
svg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis) | |
.selectAll("text") | |
.attr("dy", ".5em") | |
.attr("transform", "rotate(-30)") | |
.style("text-anchor", "end"); | |
svg.append("g") | |
.attr("class", "y axis") | |
.call(yAxis) | |
.append("text") | |
.attr("transform", "rotate(-90)") | |
.attr("y", -margin.left) | |
.attr("x", -(height-margin.bottom)/2) | |
.attr("dy", ".71em") | |
.style("text-anchor", "end") | |
.text("Deaths"); | |
var cause = svg.selectAll(".cause") | |
.data(stacked) | |
.enter().append("g") | |
.attr("class", "cause") | |
.style("fill", function(d, i) { return color(i); }); | |
var rectangles = cause.selectAll("rect") | |
.data(function(d) { | |
console.log("array for a rectangle"); | |
return d; }) // this just gets the array for bar segment. | |
.enter().append("rect") | |
.attr("width", xScale.rangeBand()); | |
// this just draws them in the default way, now they're appended. | |
transitionCount(); | |
drawLegend(); | |
var myTooltip = d3.select("body") | |
.append("div") | |
.attr("class", "myTooltip"); | |
var percentClicked = false; | |
d3.selectAll("input").on("change", handleFormClick); | |
// All the functions for stuff above! | |
function handleFormClick() { | |
if (this.value === "bypercent") { | |
percentClicked = true; | |
transitionPercent(); | |
} else { | |
percentClicked = false; | |
transitionCount(); | |
} | |
} | |
function makeData (age,data) { | |
return ageRange.map(function(age) { | |
return data.map(function(d) { | |
return {x: d["Causes"], y: +d[age], ageRange: age}; | |
}) | |
}); | |
} | |
function transitionPercent() { | |
yAxis.tickFormat(d3.format("%")); | |
stack.offset("expand"); // use this to get it to be relative/normalized! | |
var stacked = stack(makeData(ageRange, data)); | |
// call function to do the bars, which is same across both formats. | |
transitionRects(stacked); | |
} | |
function transitionCount() { | |
yAxis.tickFormat(d3.format(".2s")); // for the stacked totals version | |
stack.offset("zero"); | |
var stacked = stack(makeData(ageRange, data)); | |
transitionRects(stacked); | |
console.log("stacked", stacked); | |
} | |
function transitionRects(stacked) { | |
// this domain is using the last of the stacked arrays, which is the last illness, and getting the max height. | |
yScale.domain([0, d3.max(stacked[stacked.length-1], function(d) { return d.y0 + d.y; })]); | |
// attach new fixed data | |
var cause = svg.selectAll(".cause") | |
.data(stacked); | |
// same on the rects | |
cause.selectAll("rect") | |
.data(function(d) { | |
console.log("array for a rectangle"); | |
return d; | |
}) // this just gets the array for bar segment. | |
svg.selectAll("g.cause rect") | |
.transition() | |
.duration(250) | |
.attr("x", function(d) { | |
return xScale(d.x); }) | |
.attr("y", function(d) { | |
return yScale(d.y0 + d.y); }) // | |
.attr("height", function(d) { | |
return yScale(d.y0) - yScale(d.y0 + d.y); }); // height is base - tallness | |
svg.selectAll(".y.axis").transition().call(yAxis); | |
} | |
// Building a legend by hand, based on http://bl.ocks.org/mbostock/3886208 | |
function drawLegend() { | |
var legend = svg.selectAll(".legend") | |
.data(color.domain().slice()) // what do you think this does? | |
.enter().append("g") | |
.attr("class", "legend") | |
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; }); | |
legend.append("rect") | |
.attr("x", width) | |
.attr("width", 18) | |
.attr("height", 18) | |
.style("fill", color); | |
legend.append("text") | |
.attr("x", width + 24) | |
.attr("y", 9) | |
.attr("dy", ".35em") | |
.style("text-anchor", "start") | |
.text(function(d, i) { return ageRange[i]; }); | |
} | |
rectangles | |
.on("mouseover", mouseoverFunc) | |
.on("mousemove", mousemoveFunc) | |
.on("mouseout", mouseoutFunc); | |
function mouseoverFunc(d) { | |
console.log("moused over", d.x); | |
if(percentClicked) { | |
myTooltip | |
.style("display", null) | |
.html("<p><span class='tooltipHeader'>" + d.x + "</span><br>"+ d.ageRange + ": " + d3.format("%")(d.y) + "</p>"); | |
} else { | |
console.log("method", d.ageRange, "percent", d.y); | |
myTooltip | |
.style("display", null) | |
.html("<p><span class='tooltipHeader'>" + d.x + "</span><br>"+ d.ageRange + ": " +d.y + "</p>"); | |
} | |
} | |
function mousemoveFunc(d) { | |
myTooltip | |
.style("top", (d3.event.pageY - 5) + "px") | |
.style("left", (d3.event.pageX + 10) + "px"); | |
} | |
function mouseoutFunc(d) { | |
return myTooltip.style("display", "none"); // this sets it to invisible! | |
} | |
}); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment