Last active
August 5, 2019 23:07
-
-
Save vbernardes/9493fd7ae396cd9e59223029771aa216 to your computer and use it in GitHub Desktop.
v3.0 – Risk on Prosper Loans
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
license: gpl-3.0 | |
height: 1045 |
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
<html> | |
<meta charset="UTF-8"> | |
<head> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="http://dimplejs.org/dist/dimple.v2.3.0.min.js"></script> | |
<link rel="stylesheet" type="text/css" href="vizstyle.css"> | |
</head> | |
<body> | |
<div class="viz-title-container"> | |
<div class="viz-title"> | |
High Risks and Low Returns | |
</div> | |
<div class="viz-description"> | |
<p>Prosper is a P2P lending platform that allows investors to choose among personal loans to invest in. They do that by considering a number of factors, which include a custom calculated <span style="font-weight: normal">Prosper Score</span> that represents the risk for each loan. The higher the score, the lower the risk, and the lower the interest rates paid by the borrower to the investors.</p> | |
<p>However, on average, loans with a Prosper Score of 2-4 <i>(higher risk expected)</i> had similar proportions of bad loans (defaulted loans, charged off loans or loans past due) as loans with a score of 6-8 <i>(lower risk expected)</i>. In other words, investors who invested in loans with a score of 6-8 expected <i>lower risk</i> loans — and received <i>lower rates</i> in return — but ended up with risky loans just the same.</p> | |
<p><i>NOTE: Each point in the chart below represents one Prosper Score value, ranging from 1 to 11.</i></p> | |
</div> | |
</div> | |
<div id="chartContainer"> | |
</div> | |
<script type="text/javascript"> | |
var makeAnnotationBox = function (text) { | |
var margin = 10; | |
var annotationGroup = d3.select(".annotation-group"); | |
// Left side at circle 8 | |
var circle8 = d3.select("circle.dimple-8"); | |
var x = parseInt(circle8.attr("cx") - circle8.attr("r") - margin); | |
// Top at circle 2 | |
var circle2 = d3.select("circle.dimple-2"); | |
var y = parseInt(circle2.attr("cy") - circle2.attr("r") - margin); | |
// Right side at circle 5 | |
var circle5 = d3.select("circle.dimple-5"); | |
var width = parseInt(circle5.attr("cx")) + parseInt(circle5.attr("r")) + margin - x; | |
var height = parseInt(circle8.attr("cy")) + parseInt(circle8.attr("r")) + margin - y; | |
var box = annotationGroup.append("rect") | |
.attr("class", "annotation-box") | |
.attr("x",x).attr("y",y) | |
.attr("width",width).attr("height",height) | |
.attr("stroke-dasharray", "5,5") | |
.style("fill","transparent") | |
.style("stroke","lightgrey").style("stroke-width","2") | |
.style("fill-opacity","0.1").style("stroke-opacity","0.9"); | |
// Draw text | |
var txt = annotationGroup.append("text") | |
.classed("annotation", true) | |
.classed("current-annotation", true) | |
.style("font-family", "sans-serif") | |
.style("font-size", "13px") | |
.style("font-weight", "lighter") | |
.style("fill", "grey") | |
.text(text); | |
var txtElem = document.getElementsByClassName("current-annotation"); | |
var bbox = txtElem[0].getBBox(); | |
var halfway = x + width/2; | |
txt.attr("x",halfway - bbox.width/2).attr("y",y - margin); | |
txt.classed("current-annotation", false); | |
} | |
var makeAnnotationArrow = function (text) { | |
var margin = 10; | |
var annotationGroup = d3.select(".annotation-group"); | |
var box = d3.select(".annotation-box"); | |
var x = parseInt(box.attr("x")) + parseInt(box.attr("width")) + margin*1.5; | |
var y1 = parseInt(box.attr("y")); | |
var y2 = parseInt(box.attr("y")) + parseInt(box.attr("height")); | |
// Make arrow | |
annotationGroup.append("svg:defs").append("svg:marker") | |
.attr("id", "arrow") | |
.attr("refX", 2) | |
.attr("refY", 2) | |
.attr("markerWidth", 6) | |
.attr("markerHeight", 6) | |
.attr("orient", "auto") | |
.append("path") | |
.attr("d", "M0 0 L0 4 L4 2 Z") | |
.style("fill", "lightgrey"); | |
// Draw line | |
annotationGroup.append("line") | |
.attr("x1", x) | |
.attr("x2", x) | |
.attr("y1", y1) | |
.attr("y2", y2 - 4) // stop the line a little short so as not to show after arrow | |
.style("stroke", "lightgrey") | |
.style("stroke-width", "2") | |
.attr("marker-end", "url(#arrow)"); | |
// Draw text | |
var txt = annotationGroup.append("text") | |
.classed("annotation", true) | |
.classed("current-annotation", true) | |
.style("font-family", "sans-serif") | |
.style("font-size", "13px") | |
.style("font-weight", "lighter") | |
.style("fill", "grey") | |
.text(text); | |
var txtElem = document.getElementsByClassName("current-annotation"); | |
var bbox = txtElem[0].getBBox(); | |
var halfway = (y1 + y2)/2; | |
txt.attr("x",x + margin/2).attr("y",halfway); | |
txt.classed("current-annotation", false); | |
} | |
var drawAnnotations = function () { | |
d3.selectAll("svg") | |
.select(".dimple-chart") | |
.insert("g", ":first-child") // putting it first so as not | |
// to get in way of tooltips | |
.attr("class", "annotation-group"); | |
makeAnnotationBox("Similar Risk"); | |
makeAnnotationArrow("Descending Reward"); | |
} | |
function draw(data) { | |
// Set chart limits | |
var chartContainerWidth = 600; | |
var chartContainerHeight = 600; | |
var chartBoundsX = "10%"; | |
var chartBoundsY = "10%"; | |
var chartBoundsWidth = "85%"; | |
var chartBoundsHeight = "75%"; | |
// Set up chart | |
var svg = dimple.newSvg("#chartContainer", chartContainerWidth, chartContainerHeight); | |
var chart = new dimple.chart(svg, data); | |
chart.setBounds(chartBoundsX, | |
chartBoundsY, | |
chartBoundsWidth, | |
chartBoundsHeight); | |
chart.defaultColors = [ | |
new dimple.color("lightgrey") | |
]; | |
// Set up axes | |
var x = chart.addMeasureAxis("x", "PropBadPerScore"); | |
x.title = "Proportion of Bad Loans"; | |
x.overrideMax = 0.35; | |
var y = chart.addMeasureAxis("y", "MeanRatePerScore"); | |
y.title = "Mean Interest Rate"; | |
y.overrideMax = 0.35; | |
// Add data series | |
series = chart.addSeries("ProsperScore", dimple.plot.bubble); | |
lineSeries = chart.addSeries("ProsperScore", dimple.plot.line); | |
lineSeries.addOrderRule("ProsperScore"); | |
series.afterDraw = function (shape, data) { | |
var shape = d3.select(shape); | |
// Set circle size and color | |
shape.attr("r", 9).style("fill", "#B1B1B1"); | |
// Set bubble labels (Prosper Score) | |
svg.append("text") | |
.attr("x", parseFloat(shape.attr("cx"))) | |
.attr("y", parseFloat(shape.attr("cy")) + 4) | |
.attr("class", "bubble-label") | |
.text(data.aggField[0]); | |
}; | |
// Set tooltip | |
series.getTooltipText = function (e) { | |
return [ | |
"Proportion of Bad Loans: "+e.xValue.toFixed(2), | |
"Mean Interest Rate: "+e.yValue.toFixed(2) | |
]; | |
}; | |
chart.draw(); | |
drawAnnotations(); | |
// Hide all gridlines | |
d3.selectAll(".dimple-gridline").selectAll(".tick").html(""); | |
} | |
</script> | |
<script type="text/javascript"> | |
// Read data using d3 and pass it to the draw function | |
d3.csv("prosper_loans_export.csv", draw); | |
</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
ID | ProsperScore | MeanRatePerScore | PropBadPerScore | |
---|---|---|---|---|
1 | 4 | 0.225381286224692 | 0.100277888050814 | |
2 | 5 | 0.229148282890044 | 0.137980230306736 | |
3 | 2 | 0.27124604578564 | 0.121227887617066 | |
4 | 6 | 0.206216924580551 | 0.116061247760222 | |
5 | 7 | 0.185063461356988 | 0.0956874587147306 | |
6 | 3 | 0.24785846637006 | 0.0969641455116462 | |
7 | 1 | 0.302066633064516 | 0.314516129032258 | |
8 | 9 | 0.125149573144263 | 0.0651135870351613 | |
9 | 8 | 0.151730482037667 | 0.0861196382643325 | |
10 | 10 | 0.0979689894736842 | 0.0229473684210526 | |
11 | 11 | 0.0932809752747253 | 0.00206043956043956 |
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
.viz-title-container { | |
width: 550px; | |
height: 215px; | |
padding: 3%; | |
} | |
.viz-title { | |
font-size: 24px; | |
font-family: sans-serif; | |
margin-bottom: 2%; | |
font-weight: lighter; | |
} | |
.viz-description { | |
font-size: 15px; | |
font-family: sans-serif; | |
font-weight: lighter; | |
} | |
.annotation { | |
font-size: 12px; | |
font-family: sans-serif; | |
font-weight: lighter; | |
} | |
.bubble-label { | |
text-anchor: middle; | |
font-size: 12px; | |
font-family: sans-serif; | |
/*font-weight: lighter;*/ | |
opacity: 0.9; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment