Skip to content

Instantly share code, notes, and snippets.

@DimsumPanda
Last active August 1, 2016 15:42
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 DimsumPanda/2030ad010e69b5b3e3d2e0e1ec90c3af to your computer and use it in GitHub Desktop.
Save DimsumPanda/2030ad010e69b5b3e3d2e0e1ec90c3af to your computer and use it in GitHub Desktop.
Stacked Bars D3 v3
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet"></link>
</head>
<body>
<h2>Segments</h2>
<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 Segments</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 marginStacked = {top: 20, right: 150, bottom: 50, left: 40},
widthStacked = 600 - marginStacked.left - marginStacked.right,
heightStacked = 500 - marginStacked.top - marginStacked.bottom;
var xScale = d3.scale.ordinal()
.rangeRoundBands([0, heightStacked], .3);
var yScale = d3.scale.linear()
.rangeRound([heightStacked, 0]);
var color = d3.scale.ordinal().range(["#ff6600","#ffb380","#003399","#80aff", "#ffcc00", "#ffe680", "#339933", "#9fdf9f"]);
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(); // default view is "zero" for the count display.
var svg = d3.select("#chart").append("svg")
.attr("width", widthStacked + marginStacked.left + marginStacked.right)
.attr("height", heightStacked + marginStacked.top + marginStacked.bottom)
.append("g")
.attr("transform", "translate(" + marginStacked.left + "," + marginStacked.top + ")");
var tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip");
var percentClicked = false;
d3.csv("segments_table.csv", function(error, data) {
data.sort(function(a,b) { return +a.total - +b.total;});
var segmentsStacked = ["M_lessthan25","F_lessthan25","M_25-50","F_25-50","M_50-75","F_50-75","Mgreaterthan75", "F_greaterthan75"];
var stacked = stack(makeData(segmentsStacked, data));
xScale.domain(data.map(function(d) { return d.ethnicity; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + heightStacked + ")")
.call(xAxis)
.selectAll("text")
.attr("dy", "1em")
.attr("dx", "1em")
.style("text-anchor", "end");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("segmentsStacked");
var ethnicity = svg.selectAll(".ethnicity")
.data(stacked)
.enter().append("g")
.attr("class", "ethnicity")
.style("fill", function(d, i) { return color(i); });
var rectangles = ethnicity.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();
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(segmentsStacked, data) {
return segmentsStacked.map(function(component) {
return data.map(function(d) {
return {x: d["ethnicity"], y: +d[component], component: component};
})
});
}
function transitionPercent() {
yAxis.tickFormat(d3.format("%"));
stack.offset("expand"); // use this to get it to be relative/normalized!
var stacked = stack(makeData(segmentsStacked, 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(segmentsStacked, data));
transitionRects(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 ethnicity = svg.selectAll(".ethnicity")
.data(stacked);
// same on the rects
ethnicity.selectAll("rect")
.data(function(d) {
console.log("array for a rectangle");
return d;
}) // this just gets the array for bar segment.
svg.selectAll("g.ethnicity 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 labels = ["M < 25","F < 25", "M 25-50","F 25-50","M 50-75","F 50-75","M > 75", "F > 75"];
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," + Math.abs((i-8) * 20) + ")"; });
// Added the absolute value and transition. I reversed the names, so that I can continue to use category20(), but have health as green to make it stand out.
legend.append("rect")
.attr("x", widthStacked)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", widthStacked + 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) { return labels[i]; });
}
// ================================================================
// Mouse Events
// ================================================================
rectangles
.on("mouseover", mouseoverFunc)
.on("mousemove", mousemoveFunc)
.on("mouseout", mouseoutFunc);
function mouseoverFunc(d) {
console.log("moused over", d.x);
if(percentClicked) {
tooltip
.style("display", null)
.html("<p><span class='tooltipHeader'>" + d3.format("%")(d.y) + "</p>");
// .html("<p><span class='tooltipHeader'>" + d.x + "</span><br>"+ d.component + ": " + d3.format("%")(d.y) + "</p>");
} else {
console.log("segmentsStacked", d.component, "percent", d.y);
tooltip
.style("display", null)
.html("<p><span class='tooltipHeader'>" +d.y + "</p>");
// .html("<p><span class='tooltipHeader'>" + d.x + "</span><br>"+ d.component + ": " +d.y + "</p>");
}
}
function mousemoveFunc(d) {
tooltip
.style("top", (d3.event.pageY - 5) + "px")
.style("left", (d3.event.pageX + 10) + "px");
}
function mouseoutFunc(d) {
return tooltip.style("display", "none"); // this sets it to invisible!
}
});
</script>
ethnicity F_lessthan25 F_greaterthan75 F_25-50 F_50-75 M_lessthan25 Mgreaterthan75 M_25-50 M_50-75
Asian 52 24 193 202 88 28 188 191
Black 136 48 391 413 147 62 448 383
Hispanic 114 36 324 313 113 37 308 311
White 411 110 1029 1126 413 159 1071 1131
body {
font: 12px sans-serif;
padding: 50px;
}
svg {
background-color: rgb(255, 255, 255);
}
svg rect:hover {
opacity: .7;
}
svg g {
fill: #ACACAC;
}
svg g text.label,
svg g text.ylabel {
fill: #2A2A2A;
}
svg {
background-color: #fff;
}
#form {
position: relative;
right: 10px;
top: 10px;
padding-bottom: 20px;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.line {
stroke: #FF9900;
fill: none;
stroke-opacity: 25%;
stroke-width: 1px;
}
.unfocused {
stroke-opacity: 25%;
}
.focused {
stroke-width: 2px;
stroke-opacity: 100%;
}
.bar {
fill: steelblue;
}
.x.axis path {
/*display: none;*/
}
.tooltip {
position: absolute;
z-index: 10;
}
.tooltip p {
background-color: rgba(255,255,255,1);
padding: .5em 1em;
font-size: 12px;
line-height: 17px;
color: black;
}
.tooltipHeader {
font-weight: 700;
font-size: 12.5px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment