Skip to content

Instantly share code, notes, and snippets.

@vbernardes
Last active August 5, 2019 23:07
Show Gist options
  • Save vbernardes/9493fd7ae396cd9e59223029771aa216 to your computer and use it in GitHub Desktop.
Save vbernardes/9493fd7ae396cd9e59223029771aa216 to your computer and use it in GitHub Desktop.
v3.0 – Risk on Prosper Loans
license: gpl-3.0
height: 1045
<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>
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
.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