Last active
June 22, 2016 05:15
-
-
Save Lulkafe/54219f02b4e1624d664b0913550da143 to your computer and use it in GitHub Desktop.
Visualization of Student Loan Complain dataset
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 lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Title</title> | |
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
<script src="vis.js"></script> | |
</head> | |
<style> | |
.axis text { | |
font: 10px sans-serif; | |
} | |
.axis line, | |
.axis path { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
</style> | |
<body> | |
<div id="vis"></div> | |
<script> | |
var width = 800, | |
height = 400, | |
svg = d3.select("#vis").append("svg").attr("width",width).attr("height",height); | |
d3.csv("sloanComp.csv",function(_data){ | |
var data = []; | |
_data.forEach(function(datum) { | |
var tmp = datum.date.split('-'); | |
data.push({date: new Date(+tmp[1], +tmp[0]), count: +datum.count, category: datum.issue}); | |
}); | |
var vis = new dataVis(); | |
svg.datum(data).call(vis.width(width).height(height)); | |
}); | |
</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
date | issue | count | |
---|---|---|---|
3-2012 | D | 1 | |
3-2012 | P | 139 | |
3-2012 | G | 14 | |
3-2012 | R | 368 | |
3-2012 | C | 2 | |
4-2012 | D | 0 | |
4-2012 | P | 66 | |
4-2012 | G | 7 | |
4-2012 | R | 177 | |
4-2012 | C | 0 | |
5-2012 | D | 0 | |
5-2012 | P | 62 | |
5-2012 | G | 3 | |
5-2012 | R | 109 | |
5-2012 | C | 0 | |
6-2012 | D | 1 | |
6-2012 | P | 146 | |
6-2012 | G | 23 | |
6-2012 | R | 400 | |
6-2012 | C | 0 | |
7-2012 | D | 0 | |
7-2012 | P | 66 | |
7-2012 | G | 6 | |
7-2012 | R | 170 | |
7-2012 | C | 0 | |
8-2012 | D | 0 | |
8-2012 | P | 69 | |
8-2012 | G | 14 | |
8-2012 | R | 135 | |
8-2012 | C | 0 | |
9-2012 | D | 0 | |
9-2012 | P | 72 | |
9-2012 | G | 8 | |
9-2012 | R | 106 | |
9-2012 | C | 0 | |
10-2012 | D | 1 | |
10-2012 | P | 89 | |
10-2012 | G | 14 | |
10-2012 | R | 162 | |
10-2012 | C | 0 | |
11-2012 | D | 0 | |
11-2012 | P | 75 | |
11-2012 | G | 7 | |
11-2012 | R | 143 | |
11-2012 | C | 0 | |
12-2012 | D | 1 | |
12-2012 | P | 56 | |
12-2012 | G | 8 | |
12-2012 | R | 122 | |
12-2012 | C | 0 | |
1-2013 | D | 0 | |
1-2013 | P | 85 | |
1-2013 | G | 15 | |
1-2013 | R | 165 | |
1-2013 | C | 0 | |
2-2013 | D | 3 | |
2-2013 | P | 73 | |
2-2013 | G | 9 | |
2-2013 | R | 149 | |
2-2013 | C | 0 | |
3-2013 | D | 0 | |
3-2013 | P | 76 | |
3-2013 | G | 3 | |
3-2013 | R | 161 | |
3-2013 | C | 0 | |
4-2013 | D | 2 | |
4-2013 | P | 63 | |
4-2013 | G | 8 | |
4-2013 | R | 163 | |
4-2013 | C | 1 | |
5-2013 | D | 1 | |
5-2013 | P | 86 | |
5-2013 | G | 10 | |
5-2013 | R | 104 | |
5-2013 | C | 0 | |
6-2013 | D | 0 | |
6-2013 | P | 70 | |
6-2013 | G | 9 | |
6-2013 | R | 104 | |
6-2013 | C | 1 | |
7-2013 | D | 0 | |
7-2013 | P | 82 | |
7-2013 | G | 12 | |
7-2013 | R | 155 | |
7-2013 | C | 0 | |
8-2013 | D | 2 | |
8-2013 | P | 74 | |
8-2013 | G | 7 | |
8-2013 | R | 177 | |
8-2013 | C | 0 | |
9-2013 | D | 1 | |
9-2013 | P | 63 | |
9-2013 | G | 6 | |
9-2013 | R | 183 | |
9-2013 | C | 0 | |
10-2013 | D | 0 | |
10-2013 | P | 67 | |
10-2013 | G | 12 | |
10-2013 | R | 231 | |
10-2013 | C | 1 | |
11-2013 | D | 1 | |
11-2013 | P | 69 | |
11-2013 | G | 5 | |
11-2013 | R | 190 | |
11-2013 | C | 3 | |
12-2013 | D | 59 | |
12-2013 | P | 50 | |
12-2013 | G | 7 | |
12-2013 | R | 149 | |
12-2013 | C | 38 | |
1-2014 | D | 187 | |
1-2014 | P | 0 | |
1-2014 | G | 6 | |
1-2014 | R | 2 | |
1-2014 | C | 159 | |
2-2014 | D | 187 | |
2-2014 | P | 1 | |
2-2014 | G | 7 | |
2-2014 | R | 1 | |
2-2014 | C | 153 | |
3-2014 | D | 208 | |
3-2014 | P | 0 | |
3-2014 | G | 7 | |
3-2014 | R | 3 | |
3-2014 | C | 185 | |
4-2014 | D | 179 | |
4-2014 | P | 0 | |
4-2014 | G | 9 | |
4-2014 | R | 0 | |
4-2014 | C | 177 | |
5-2014 | D | 184 | |
5-2014 | P | 1 | |
5-2014 | G | 4 | |
5-2014 | R | 1 | |
5-2014 | C | 157 | |
6-2014 | D | 178 | |
6-2014 | P | 0 | |
6-2014 | G | 13 | |
6-2014 | R | 0 | |
6-2014 | C | 161 | |
7-2014 | D | 192 | |
7-2014 | P | 0 | |
7-2014 | G | 2 | |
7-2014 | R | 0 | |
7-2014 | C | 162 | |
8-2014 | D | 198 | |
8-2014 | P | 0 | |
8-2014 | G | 9 | |
8-2014 | R | 0 | |
8-2014 | C | 135 | |
9-2014 | D | 189 | |
9-2014 | P | 0 | |
9-2014 | G | 20 | |
9-2014 | R | 0 | |
9-2014 | C | 155 | |
10-2014 | D | 193 | |
10-2014 | P | 0 | |
10-2014 | G | 6 | |
10-2014 | R | 0 | |
10-2014 | C | 173 | |
11-2014 | D | 179 | |
11-2014 | P | 0 | |
11-2014 | G | 7 | |
11-2014 | R | 0 | |
11-2014 | C | 155 | |
12-2014 | D | 182 | |
12-2014 | P | 0 | |
12-2014 | G | 10 | |
12-2014 | R | 1 | |
12-2014 | C | 147 | |
1-2015 | D | 205 | |
1-2015 | P | 0 | |
1-2015 | G | 12 | |
1-2015 | R | 0 | |
1-2015 | C | 137 | |
2-2015 | D | 229 | |
2-2015 | P | 0 | |
2-2015 | G | 6 | |
2-2015 | R | 0 | |
2-2015 | C | 137 | |
3-2015 | D | 277 | |
3-2015 | P | 0 | |
3-2015 | G | 12 | |
3-2015 | R | 0 | |
3-2015 | C | 143 | |
4-2015 | D | 265 | |
4-2015 | P | 0 | |
4-2015 | G | 19 | |
4-2015 | R | 0 | |
4-2015 | C | 145 | |
5-2015 | D | 242 | |
5-2015 | P | 0 | |
5-2015 | G | 13 | |
5-2015 | R | 0 | |
5-2015 | C | 129 | |
6-2015 | D | 249 | |
6-2015 | P | 0 | |
6-2015 | G | 8 | |
6-2015 | R | 0 | |
6-2015 | C | 119 | |
7-2015 | D | 231 | |
7-2015 | P | 0 | |
7-2015 | G | 11 | |
7-2015 | R | 1 | |
7-2015 | C | 168 | |
8-2015 | D | 241 | |
8-2015 | P | 0 | |
8-2015 | G | 21 | |
8-2015 | R | 0 | |
8-2015 | C | 148 | |
9-2015 | D | 260 | |
9-2015 | P | 0 | |
9-2015 | G | 12 | |
9-2015 | R | 0 | |
9-2015 | C | 127 | |
10-2015 | D | 233 | |
10-2015 | P | 0 | |
10-2015 | G | 5 | |
10-2015 | R | 0 | |
10-2015 | C | 116 | |
11-2015 | D | 170 | |
11-2015 | P | 0 | |
11-2015 | G | 9 | |
11-2015 | R | 0 | |
11-2015 | C | 107 | |
12-2015 | D | 177 | |
12-2015 | P | 0 | |
12-2015 | G | 13 | |
12-2015 | R | 0 | |
12-2015 | C | 104 | |
1-2016 | D | 220 | |
1-2016 | P | 0 | |
1-2016 | G | 13 | |
1-2016 | R | 0 | |
1-2016 | C | 100 | |
2-2016 | D | 222 | |
2-2016 | P | 0 | |
2-2016 | G | 11 | |
2-2016 | R | 0 | |
2-2016 | C | 106 | |
3-2016 | D | 244 | |
3-2016 | P | 0 | |
3-2016 | G | 7 | |
3-2016 | R | 0 | |
3-2016 | C | 108 | |
4-2016 | D | 239 | |
4-2016 | P | 0 | |
4-2016 | G | 15 | |
4-2016 | R | 0 | |
4-2016 | C | 134 | |
5-2016 | D | 12 | |
5-2016 | P | 0 | |
5-2016 | G | 0 | |
5-2016 | R | 0 | |
5-2016 | C | 2 |
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 dataVis () { | |
var x = 0, | |
y = 0, | |
padding = { top: 40, bottom: 40, left: 20, right: 20}, | |
width = 700, | |
height = 400; | |
function vis (selection) { | |
var data = selection.datum(), | |
timeScaleForAxis = d3.time.scale(), //Scale for just showing the x (time) axis | |
timeScale = d3.scale.ordinal(), //Actual scale to determine x coordinate | |
categScale = d3.scale.ordinal(), | |
categories = d3.set(data.map(function(d){ return d.category; })).values(), | |
colorScale = d3.scale.linear(), | |
dates = data.filter(function(d){ | |
return d.category == categories[0]; | |
}).map(function(d){ return d.date; }), | |
line = d3.svg.line(), | |
rateScale = d3.scale.linear(), | |
xAxis = d3.svg.axis(), | |
oldest = d3.min(dates), | |
newest = d3.max(dates), | |
countMax = d3.max(data.map(function(d){ return d.count; })), | |
borderWidth = 0.1, | |
monthText = ["January", "Febrary", "March", "April", "May", "June", | |
"July", "August", "September", "Octorber", "November", "December"], | |
/* Text is same as the original values */ | |
categText = { | |
"D" : "Dealing with my lender or servicer", | |
"P" : "Problems when you are unable to pay", | |
"G" : "Getting a loan", | |
"R" : "Repaying your loan", | |
"C" : "Can't repay my loan" | |
}, | |
//From ColorBrewer2.0 | |
colors = ['#ffffff', '#ffffb2','#fed976','#feb24c','#fd8d3c','#f03b20','#bd0026'], | |
//Values are manually chose to show the data easier to see | |
colorDomain = [0, 1, 24, 90, 210, 278, countMax]; | |
timeScale | |
.domain(dates) | |
.rangeRoundBands([padding.left, width - padding.right]); | |
timeScaleForAxis | |
.domain([oldest, newest]) | |
.rangeRound([timeScale(oldest) + timeScale.rangeBand() / 2, | |
timeScale(newest) + timeScale.rangeBand() / 2]); | |
rateScale | |
.domain([0, countMax]) | |
.range([0.0, 1.0]); | |
colorScale | |
.domain(colorDomain) | |
.range(colors); | |
categScale | |
.domain(categories) | |
.rangeRoundBands([padding.top, height - padding.bottom], 0.4); | |
xAxis.scale(timeScaleForAxis).orient("bottom"); | |
line | |
.x(function(d){ return d; }) | |
.y(function(d){ return d; }) | |
.interpolate("linear-closed"); | |
var gParent = selection.append("g"), | |
gColumn = gParent.append("g"), | |
gValues = gParent.append("g"), | |
gCateg = gParent.append("g"), | |
gLegend = gParent.append("g"), | |
columnIDPrefix = "_backColumn", | |
labelTxtIDPrefix = "_label", | |
monthTextID = "_monthText", | |
tooltipID = "_tooltip", | |
indicatorID = "_indicator", | |
opacityMOver = 0.2, | |
opacityDefault = 0, | |
lWidth = 120, | |
lHeight = 20, | |
lTextOfs = 4, | |
indicatorWidth = 5, | |
lx = (width - padding.left - padding.right) / 1.05 - lWidth, | |
ly = padding.top / 1.3, | |
getColumn = function (i) { | |
return d3.select("#" + columnIDPrefix + Math.floor(i / categories.length)); | |
}; | |
var mouseOverEvent = function (selection) { | |
selection.style("opacity", opacityMOver); | |
}, | |
mouseLeaveEvent = function (selection) { | |
selection.style("opacity", opacityDefault); | |
d3.select("#" + monthTextID).remove(); | |
}; | |
//Draw Rects (Columns), which is highlighed when the mouse cursor is over | |
gColumn.selectAll("rect").data(data).enter().append("rect") | |
.attr("x", function(d) { return timeScale(d.date)}) | |
.attr("y", padding.top) | |
.attr("id", function(d,i) { return columnIDPrefix + Math.floor(i / categories.length); }) | |
.attr("height", height - (padding.top + padding.bottom)) | |
.attr("width", timeScale.rangeBand()) | |
.style({ | |
"fill": "gray", | |
"opacity": opacityDefault | |
}) | |
.on("mouseover", function() { | |
mouseOverEvent(d3.select(this)); | |
}) | |
.on("mouseleave", function() { | |
mouseLeaveEvent(d3.select(this)); | |
}); | |
//Draw Rects (Values) | |
gValues.selectAll("rect").data(data).enter().append("rect") | |
.attr("x", function(d) { return timeScale(d.date); }) | |
.attr("y", function(d) { return categScale(d.category); }) | |
.attr("height", categScale.rangeBand()) | |
.attr("width", timeScale.rangeBand()) | |
//.attr("width", dummyTimeScale.rangeBand()) | |
.style({ | |
"fill" : "white", | |
"stroke-width": borderWidth, | |
"stroke": "black", | |
"rx" : 0, | |
"ry" : 0 | |
}) | |
.on("mouseover", function(d, i) { | |
var rect = d3.select(this); | |
mouseOverEvent(getColumn(i)); | |
d3.select(this).node().parentNode.appendChild(d3.select(this).node()); | |
d3.select(this).style({ "stroke-width" : 1 }); | |
d3.select("#" + labelTxtIDPrefix + d.category) | |
.transition().duration(100).style({ | |
"font-size" : "17px" | |
}); | |
//Add and show tooltip if not exist yet | |
if (gParent.select("#" + tooltipID).empty()) { | |
var gTooltip = gParent.append("g").attr("id", tooltipID), | |
rectX = +rect.attr("x"), | |
rectY = +rect.attr("y"), | |
rectH = +rect.attr("height"), | |
rectW = +rect.attr("width"), | |
ofs = 4, | |
tWidth = 130, | |
tHeight = 60, | |
//Top-left point of the tooltip | |
newX = rectX + rectW + ofs, | |
newY = rectY + rectH + ofs; | |
if (width - padding.left - padding.right < newX + tWidth) | |
newX = rectX - tWidth - ofs; | |
if (height < newY + tHeight) | |
newY = rectY - tHeight - ofs; | |
gTooltip.append("rect") | |
.attr("x", newX) | |
.attr("y", newY) | |
.attr("width", tWidth) | |
.attr("height", tHeight) | |
.style({ | |
"fill": "white", | |
"stroke-width": 2, | |
"stroke": "black", | |
"rx": 3, | |
"ry": 3 | |
}); | |
//Add Date text to tooltip | |
gTooltip.append("text") | |
.attr("x", newX + tWidth / 2) | |
.attr("y", newY + tHeight / 3.2) | |
.text(function(){ | |
var date = rect.datum().date; | |
return monthText[date.getMonth()] + ", " + date.getFullYear(); | |
}) | |
.style({ | |
"font-family": "sans-serif", | |
"font-size": "13px", | |
"text-anchor": "middle" | |
}); | |
//Add count text to tooltip | |
gTooltip.append("text") | |
.attr("x", newX + tWidth / 2) | |
.attr("y", newY + (tHeight - (tHeight / 3))) | |
.text(function(){ | |
var count = +rect.datum().count; | |
return count + " issue" + ((count == 0 || count == 1)? "" : "s"); | |
}) | |
.style({ | |
"font-family": "sans-serif", | |
"font-size": "17px", | |
"text-anchor": "middle" | |
}); | |
} | |
d3.select("#" + indicatorID) | |
.attr("x", function(){ | |
var count = +rect.datum().count; | |
return lx + (lWidth * rateScale(count)) - (indicatorWidth / 2); | |
}) | |
.style("opacity", 1) | |
}) | |
.on("mouseleave", function(d, i) { | |
mouseLeaveEvent(getColumn(i)); | |
d3.select(this).style({ "stroke-width": 0.1 }); | |
d3.select("#" + labelTxtIDPrefix + d.category) | |
.transition().duration(300).style({ | |
"font-size" : "13px" | |
}); | |
d3.select("#" + indicatorID).style("opacity", 0); | |
gParent.select("#" + tooltipID).remove(); | |
}); | |
//Animation for rect colors | |
gValues.selectAll("rect").transition().delay(function(d,i){ return i * 5; }).duration(800) | |
.style("fill", function(d){ | |
return colorScale(d.count); | |
}); | |
//Show category names | |
gCateg.selectAll("text").data(categories).enter().append("text") | |
.attr("x", padding.left) | |
.attr("y", function(d) { return categScale(d) - 4; }) | |
.attr("id", function(d) { return labelTxtIDPrefix + d; }) | |
//.transition().delay(1500) | |
.text(function(d) { return categText[d]; }) | |
.style({ | |
"font-size" : "13px", | |
"font-family" : "sans-serif" | |
}); | |
//Show time axis at the bottom | |
gParent.append("g") | |
.attr("class","x axis") | |
.attr("transform", "translate(0," + (height - padding.bottom) + ")") | |
.call(xAxis); | |
//Color scheme for legend | |
var gradient = gLegend.append("defs") | |
.append("linearGradient") | |
.attr("id", "legendGradient") | |
.attr("x1", "0%") | |
.attr("y1", "100%") | |
.attr("x2", "100%") | |
.attr("y2", "100%") | |
.attr("spreadmethond", "pad"); | |
gradient.selectAll("stop").data(colorDomain).enter().append("stop") | |
.attr("offset", function(d){ | |
return rateScale(d) * 100 + "%"; | |
}) | |
.attr("stop-color", function(d,i){ | |
return colors[i]; | |
}) | |
.attr("stop-opacity", 1); | |
//Draw legend | |
gLegend.append("rect") | |
.attr("x", lx) | |
.attr("y", ly) | |
.transition() | |
.delay(1300) | |
.attr("width", lWidth) | |
.attr("height", lHeight) | |
.style({ | |
"fill" : "url(#legendGradient)", | |
"stroke-width" : 0.3, | |
"stroke" : "black", | |
"rx" : 0.5, | |
"ry" : 0.5 | |
}); | |
//Min label for legend | |
gLegend.append("text") | |
.attr("x", lx) | |
.attr("y", ly - lTextOfs) | |
.transition() | |
.delay(1300) | |
.text("Min (0)") | |
.style({ | |
"font-family" : "sans-serif", | |
"font-size" : "10px", | |
"text-anchor" : "middle" | |
}); | |
//Max label for legend | |
gLegend.append("text") | |
.attr("x", lx + lWidth + lTextOfs) | |
.attr("y", ly - lTextOfs) | |
.transition() | |
.delay(1300) | |
.text("Max (" + countMax + ")") | |
.style({ | |
"font-family" : "sans-serif", | |
"font-size" : "10px", | |
"text-anchor" : "middle" | |
}); | |
//Draw Indicator, which shows the current value (count) | |
gLegend.append("rect") | |
.attr("x", lx + lWidth / 2) | |
.attr("y", ly) | |
.attr("id", indicatorID) | |
.attr("width", indicatorWidth) | |
.attr("height", lHeight - 1) | |
.style({ | |
"fill" : "white", | |
"stroke" : "black", | |
"stroke-width" : 1.4, | |
"opacity" : 0 | |
}); | |
} | |
vis.x = function (val) { | |
x = val; | |
return vis; | |
}; | |
vis.y = function (val) { | |
y = val; | |
return vis; | |
}; | |
vis.width = function (val) { | |
width = val; | |
return vis; | |
}; | |
vis.height = function (val) { | |
height = val; | |
return vis; | |
}; | |
vis.padding = function (val) { | |
padding = val; | |
return vis; | |
}; | |
return vis; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment