Skip to content

Instantly share code, notes, and snippets.

@mattykuch
Created February 8, 2017 12:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save mattykuch/40ba19de703632ea2afbbc5156b9471f to your computer and use it in GitHub Desktop.
Save mattykuch/40ba19de703632ea2afbbc5156b9471f to your computer and use it in GitHub Desktop.
Linked Charts (Line and Bar) with D3.js
//Store width, height and margin in variables
var wLine = 550;
var hLine = 500;
var marginLine = {top: 40, right: 10, bottom: 20, left: 50};
//Set up date formatting and years
var dateFormat = d3.time.format("%Y");
// Scale the width and height
var xScaleLine = d3.time.scale()
.range([ marginLine.left, wLine - marginLine.right - marginLine.left ]);
var yScaleLine = d3.scale.linear()
.range([ marginLine.top, hLine - marginLine.bottom]);
// Creat Axes i.e. xAxis and yAxis
var xAxisLine = d3.svg.axis()
.scale(xScaleLine)
.orient("bottom")
.ticks(5)
.tickFormat(function(d) {
return dateFormat(d);
});
var yAxisLine = d3.svg.axis()
.scale(yScaleLine)
.orient("left");
// Setting x position for line labels
var xLabelLine = wLine - marginLine.right - marginLine.left;
// Configure line generator
var line = d3.svg.line()
.x(function(d) {
return xScaleLine(dateFormat.parse(d.year)); // come back here and replace "year"
})
.y(function(d) {
return yScaleLine(+d.amount); // come back here and replace "amount"
})
//Create an empty svg
var linechart = d3.select("#area1")
.append("svg")
.attr("width", wLine)
.attr("height", hLine);
var dataset; // This is a Global variable
var activeDistrict; // Will be used for linked hovering
// Load in csv data
d3.csv("div9.csv", function(data) {
// Create new array of all years in timeline for linechart. Will be referenced later
var years = [ "2011", "2012", "2013", "2014", "2015"];
//Make dataset an empty array (for now) to hold our restructured dataset
dataset = [];
// Loop once for each row in data
for (var i=0; i < data.length; i++) {
//Create a new object with the district's name and empty array
dataset[i] = {
district: data[i].district,
rate: []
};
//Loop through all the years
for (var j = 0; j < years.length; j++) {
//If value is empty
if (data[i][years[j]]) {
//Add a new object to the Div 9 rate data array
//for that district
dataset[i].rate.push({
year: years[j],
amount: data[i][years[j]]
}); // end of push( function
} //end of if(
} // end of for loop for years
} // end of for loop for data
// Set scale domains
xScaleLine.domain([
d3.min(years, function(d) {
return dateFormat.parse(d);
}),
d3.max(years, function(d) {
return dateFormat.parse(d);
})
]);
yScaleLine.domain([
d3.max(dataset, function(d) {
return d3.max(d.rate, function(d) {
return +d.amount;
});
}),
0
]);
// Make a group for each district
var groups = linechart.selectAll("g")
.data(dataset)
.enter()
.append("g")
.classed("national", function(d) {
if (d.district == "UGANDA") return true;
else return false;
})
.on("mouseover", function(d) {
activeDistrict = d.district;
// Setting positio for the district label
var xPosition = wLine/2 + 35;
var yPosition = marginLine.top - 10;
linechart.append("text")
.attr("id", "hoverLabel")
.attr("x", xPosition)
.attr("y", yPosition)
.attr("text-anchor", "start")
.attr("font-family", "ff-nuvo-sc-web-pro-1,ff-nuvo-sc-web-pro-2, sans-serif")
.attr("font-size", "20px")
.text( activeDistrict);
d3.selectAll("rect")
.classed("barLight", function(d) {
if ( d.district == activeDistrict) return true;
else return false;
});
}) // end of .on mouseover
.on("mouseout", function() {
d3.select("#hoverLabel").remove();
d3.selectAll("rect")
.attr("class", "barBase");
}) // end of .on mouseout
// Append a title with the district name (for easy tooltips)
groups.append("title")
.text(function(d) {
return d.district;
});
//Within each group, create a new line/path,
//binding just the div9 rate data to each one
groups.selectAll("path")
.data(function(d) {
return [ d.rate ];
})
.enter()
.append("path")
.attr("class", "line")
.attr("d", line);
//Axes
linechart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (hLine - marginLine.bottom) + ")")
.call(xAxisLine);
linechart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + (marginLine.left) + ",0)")
.call(yAxisLine)
.append("text")
.attr("x", 0 - marginLine.left)
.attr("y", marginLine.top - 10)
.style ("text-anchor", "start")
.text("% of candidates who obtained a Division 9 in ");
//Labels for highlighted lines - probably better to wrap these into the line elements themselves
//with some logic for selecting the ones you want to highlight? Use anonymous function to match objects for highlighting?
//National label
linechart.append("text")
.attr("transform", "translate(" + xLabelLine + ", " + yScaleLine(data[20][years[4]]) + ")")
.attr("dy", ".15em")
.attr("dx", ".25em")
.attr("text-anchor", "start")
.attr("class","labelNation")
.text( + data[20][years[4]] );
});// end of d3.csv(
//Store width, height and margin in variables
var w = 600;
var h = 500;
var margin = {top: 40, right: 10, bottom: 20, left: 50};
// Scale the width and height
var xScale = d3.scale.linear()
.range([0,w - margin.right - margin.left]);
var yScale = d3.scale.ordinal()
.rangeRoundBands([margin.top, h - margin.bottom],0.2);
// Creat Axes i.e. xAxis and yAxis
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
// Create SVG
var barchart = d3.select("#area2")
.append("svg")
.attr("width", w)
.attr("height", h);
// Entering data
d3.csv("div9.csv", function(data) {
data.sort(function(a, b) {
return d3.descending(+a.avg, +b.avg)
});
//Setting a dynamic domain for the xScale based on Data
xScale.domain([5
/* d3.min(data, function(d) {
return +d.avg; })*/,
d3.max(data, function(d) {
return +d.avg;
}) ]);
//Setting a dynamic domain for the yScale based on Data
yScale.domain(data.map(function(d) { return d.district; } ));
//Rendering the xAxis
barchart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + (margin.left + 65) + "," + (h - margin.top + 20) + ")")
.call(xAxis);
//Rendering the yAxis
barchart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + (margin.left + 65) + ",0)") // Moving the axis to fit in district names
.call(yAxis)
.append("text")
.attr("x", margin.left - 65)
.attr("y", margin.top - 5)
.style("font-size", "14")
.style("text-anchor", "start")
.text("Average failure rate (in %) over the last 5 years by district");
// Rendering the rectangles
var rects = barchart.selectAll("rect")
.data(data)
.enter()
.append("rect");
rects.attr("x", margin.left + 70) // Moving the x so district name can fit
.attr("y", function(d, i) {
return yScale(d.district);
})
.attr("width", function(d) {
return xScale(d.avg);
})
.attr("height",yScale.rangeBand)
.attr("class", "barBase")
.append("title")
.text(function(d) {
return d.district + "'s failure rate over the last 5 years is " + d.avg + "%";
});
//rollover functionality
barchart.selectAll("rect")
.on("mouseover", function(d) {
activeDistrict = d.district;
linechart.selectAll("g")
.each(function(d) {
if(d){
if ( d.district == activeDistrict ){
// console.log(d3.select(this).select("path"));
d3.select(this).select("path").classed("pathLight", true);
var xPosition = wLine/2 + 35;
var yPosition = marginLine.top - 10;
linechart.append("text")
.attr("id", "hoverLabel")
.attr("x", xPosition)
.attr("y", yPosition)
.attr("text-anchor", "start")
.attr("font-family", "ff-nuvo-sc-web-pro-1,ff-nuvo-sc-web-pro-2, sans-serif")
.attr("font-size", "20px")
.text( activeDistrict);
return true;
}
else{
return false;
}
}
})
})
.on("mouseout", function() {
d3.selectAll("path")
.attr("class", "pathBase");
d3.select("#hoverLabel").remove();
});
}); // end of d3.csv
rank district 2011 2012 2013 2014 2015 avg div1
1 BUGIRI 9.68 10.27 24.90 23.74 16.69 17.66 0.71
2 BUKWO 7.04 7.74 20.19 20.76 21.59 15.68 12.65
3 BUNDIBUGYO 11.09 10.50 20.03 17.20 18.68 15.67 15.34
4 BULAMBULI 13.89 13.37 15.41 15.04 15.32 14.65 12.39
5 SIRONKO 8.66 10.33 20.38 13.08 16.96 13.93 15.89
6 BUSIA 6.70 10.44 17.91 12.46 19.80 13.54 13.66
7 MANAFWA 9.53 11.37 16.90 14.48 13.71 13.31 14.03
8 BUYENDE 10.90 8.64 12.47 14.84 18.69 13.20 11.01
9 MBALE 7.40 10.59 14.56 13.63 16.60 12.64 9.15
10 ADJUMANI 6.98 9.33 15.85 16.95 12.68 12.36 9.71
11 BUTALEJA 6.82 7.95 13.60 14.22 18.23 12.34 7.60
12 BUDUDA 9.90 13.79 15.96 10.63 11.34 12.20 8.64
13 KWEEN 7.84 8.74 16.30 11.15 13.83 11.57 7.79
14 IGANGA 7.46 9.02 14.52 11.77 14.36 11.52 5.98
15 BULIISA 2.10 3.50 16.08 12.32 18.84 11.30 8.51
16 NAKAPIRIPIRIT 7.93 5.55 16.28 9.10 16.70 11.11 7.69
17 MAYUGE 5.17 6.08 14.13 11.64 15.13 10.80 8.80
18 NEBBI 7.10 11.43 14.22 10.50 10.32 10.78 8.39
19 ZOMBO 6.67 8.80 18.97 10.31 9.14 10.78 8.65
20 YUMBE 8.57 12.27 13.67 9.00 9.67 10.67 7.74
21 UGANDA 4.27 5.04 9.05 7.17 9.62 7.06 0
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>A Profile on Failure in O'level: Highlighting 20 districts that have consistently performed poorly over the last 5 years (2011 - 2015)</title>
<!-- Custom CSS styles -->
<link href="style.css" rel="stylesheet" type="text/css" >
</head>
<body>
<div class="container">
<h2> A Profile on Failure in O'level: Highlighting 20 districts that have consistently performed poorly over the last 5 years (2011 - 2015)</h2>
<h4> .... </h4>
<div id="area1"> <!-- barchart container -->
</div>
<div id="area2"> <!-- barchart2 container -->
</div>
</div>
<div id="footer">
<strong>Source</strong> : www.data.ug
</div>
<!-- JS Libraries -->
<!--<script src="d3.js" charset="utf-8"></script>-->
<script type="text/javascript" src="http://d3js.org/d3.v3.js"></script>
<!-- Custom JS code -->
<script src="area1.js"></script>
<script src="area2.js"></script>
</body>
</html>
.container {
width: 1200px;
height: 700px;
margin: 25px auto 25px auto;
padding: 50px 50px 50px 50px;
background-color: white;
box-shadow: 0 0 20px #ccc;
}
#footer {
border-top: 1px solid silver;
color: #888888;
font-size: 1.25rem;
text-align: center;
margin-top: 1rem;
padding: 0.5rem;
}
body {
background-color: #fff;
font-family: "adelle-1","adelle-2", constantia, cambria, Georgia, serif;
font-size:14px;
margin: 18px 0 0 30px;
}
h1 {
font-size: 24px;
margin: 0;
color: #5387bd;
font-weight: normal;
}
h2 {
font-weight: normal;
color: #808080;
}
p {
color: #808080;
}
svg {
background-color: white;
}
#intro {
width: 740px;
line-height: 150%;
}
#area1 {
display: inline-block;
/* float: right;*/
}
#area2 {
display: inline-block;
/* float: left;*/
}
/*#footer {
clear: both;
width: 760px;
line-height: 150%;
font-size: 12px;
}*/
path {
stroke: #888;
stroke-width: 2;
opacity: 0.2;
-moz-transition: all 0.1s;
-o-transition: all 0.1s;
-webkit-transition: all 0.1s;
transition: all 0.1s;
cursor: pointer;
fill: none;
}
g.linklight path{
stroke: #ff0000;
opacity: 1;
}
g.highlight path {
stroke: #cfa63e;
stroke-width: 4;
opacity: 1;
}
g.national path {
stroke: #5387bd;
stroke-width: 4;
opacity: 1;
}
.axis path,
.axis line {
fill: none;
stroke: #BCBCBC;
stroke-width: 1;
shape-rendering: crispEdges;
}
.axis path {
opacity: 1;
}
.line:hover, .pathBase:hover {
stroke: black;
opacity: 1;
}
.pathBase {
stroke: #888;
opacity: 0.2;
}
.pathLight {
stroke: black;
opacity: 1;
}
#hoverlabel {
font-family: "ff-nuvo-sc-web-pro-1","ff-nuvo-sc-web-pro-2", sans-serif;
fill: #496d64;
font-size: 15px;
color: #808080;
text-anchor: start;
}
.axis text {
font-family: "ff-nuvo-sc-web-pro-1","ff-nuvo-sc-web-pro-2", sans-serif;
font-size: 14px;
letter-spacing: 0.5px;
}
.linelabel {
font-family: "ff-nuvo-sc-web-pro-1","ff-nuvo-sc-web-pro-2", sans-serif;
fill: #333;
font-size: 12px;
text-anchor: start;
opacity: 0.5;
}
.labelNation {
font-family: "ff-nuvo-sc-web-pro-1","ff-nuvo-sc-web-pro-2", sans-serif;
fill: #5387bd;
font-size: 12px;
text-anchor: start;
}
.labelDistrict {
font-family: "ff-nuvo-sc-web-pro-1","ff-nuvo-sc-web-pro-2", sans-serif;
fill: #cfa63e;
font-size: 12px;
text-anchor: start;
}
.path.active {
stroke: black;
opacity: 1;
}
/* Bar specific CSS */
rect:hover {
fill: #2E2E31;
}
.barBase {
fill: #5288BE;
}
.barLight {
fill: #2E2E31;
}
.axisBar text {
font-family: "ff-nuvo-sc-web-pro-1","ff-nuvo-sc-web-pro-2", sans-serif;
font-size: 11px;
}
.axisBarTitle {
font-family: "ff-nuvo-sc-web-pro-1","ff-nuvo-sc-web-pro-2", sans-serif;
font-size: 12px;
}
.y.axisBar path,
.y.axisBar line {
opacity: 0;
cursor: auto;
}
.x.axisBar path,
.x.axisBar line {
opacity: 0.2;
cursor: auto;
}
.barlabel {
font-family: "ff-nuvo-sc-web-pro-1","ff-nuvo-sc-web-pro-2", sans-serif;
font-size:11px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment