Skip to content

Instantly share code, notes, and snippets.

@aficionado
Last active February 6, 2016 17:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save aficionado/7538081 to your computer and use it in GitHub Desktop.
Save aficionado/7538081 to your computer and use it in GitHub Desktop.
Iso-cost lines
<!DOCTYPE html>
<meta charset="utf-8">
<style>
html, body{
height: 100%;
}
*, *:before, *:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body {
font: 14px/18px "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: 0;
position: relative;
background: #fff;
display: block;
color: #333;
}
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
.clearfix { display: inline-table; }
* html .clearfix { height: 1%; }
.clearfix { display: block; }
#wrap{
min-height: 100%;
height: auto;
margin: 0 auto -60px;
padding: 0 0 60px;
}
#header{
height: 100px;
background: #F7F7F7;
border-bottom: 1px solid #D6D8D9;
border-top: 4px solid #293A44;
}
#header img{
margin-top: 10px;
}
#footer {
height: 60px;
background-color: #293A43;
color: #5D6F79;
text-align: center;
font-size: 11px;
padding-top: 20px;
}
.container{
width: 980px;
margin: 0 auto;
padding: 0 30px;
}
#chart {
float: left;
width: 720px;
margin-top: 30px;
}
.sliders{
float: left;
width: 160px;
margin-top: 30px;
}
.slider_box{
background: #eee;
padding: 10px 10px 5px;
margin-top:20px;
border-radius:5px;
}
.sliders label{
display: block;
}
.sliders input{
float: left;
margin-bottom: 10px;
}
.sliders .value_slider{
display: block;
font-weight: bold;
text-align: center;
}
.legend{
font-size: 12px;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
</style>
<body>
<div id="wrap">
<div id="header">
<div class="container clearfix">
<a href="https://bigml.com/" target="_blank"><img alt="bigml" src="https://static.bigml.com/static/img/bigml.png"></a>
</div>
</div>
<div class="container clearfix">
<div id="chart"></div>
<div class="sliders">
<div class="clearfix slider_box">
<label>Iso-cost line:</label>
<input id="isoline" type="range" min="0" max="100" value="20">
</div>
<div class="clearfix slider_box">
<label>P(+):</label>
<input id="prevalence" type="range" min="0" max="40" value="20">
<div id="prevalencedisplay" class="value_slider">0.5</div>
</div>
<div class="clearfix slider_box">
<label>FP cost:</label>
<input id="fpcost" type="range" min="1" max="250" value="25">
<div id="fpcostdisplay" class="value_slider">0.25</div>
</div>
<div class="clearfix slider_box">
<label>FN cost:</label>
<input id="fncost" type="range" min="1" max="250" value="25">
<div id="fncostdisplay" class="value_slider">0.25</div>
</div>
</div>
</div>
</div>
<div id="footer">
Copyright © 2013 BigML, Inc.
</div>
</body>
<script src="https://code.jquery.com/jquery-1.7.1.js" type="text/javascript"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
// Computes Area Under the Curve using Heron's formula
var AUC = function(tpr, fpr) {
a = Math.sqrt(2);
b = Math.sqrt(tpr * tpr + fpr * fpr);
c = Math.sqrt((1 - tpr) * (1 - tpr) + (1 - fpr) * (1 - fpr));
s = (a + b + c) /2;
if (tpr == fpr) {
return 0.5
} else if (tpr > fpr) {
return 0.5 + Math.sqrt(s * (s - a) * (s - b) * (s - c));
} else {
return 0.5 - Math.sqrt(s * (s - a) * (s - b) * (s - c));
}
};
</script>
<script>
// Loads multiple evaluations
var loadEvaluations = function(options) {
var settings = $.extend({
callback: function() {},
maxInstances: 1,
urls: [
'https://bigml.io/andromeda/evaluation/52407497035d0772e700ed50?username=francisco;api_key=0100cfce0561be0b2cd4a3203c89d2c110c94e1d',
'https://bigml.io/andromeda/evaluation/52407637035d0772e700ed71?username=francisco;api_key=0100cfce0561be0b2cd4a3203c89d2c110c94e1d',
'https://bigml.io/andromeda/evaluation/5230b787035d0772e3003ba6?username=francisco;api_key=0100cfce0561be0b2cd4a3203c89d2c110c94e1d',
'https://bigml.io/andromeda/evaluation/52274838035d0729c1000681?username=francisco;api_key=0100cfce0561be0b2cd4a3203c89d2c110c94e1d',
'https://bigml.io/andromeda/evaluation/51fb3e5e035d072bfd00111d?username=francisco;api_key=0100cfce0561be0b2cd4a3203c89d2c110c94e1d',
],
evaluations: []
}, options || {});
$.ajax({
url : settings.urls[settings.evaluations.length],
dataType: 'jsonp',
crossDomain:true,
success: function(evaluation) {
var confusionMatrix = evaluation.result.model.confusion_matrix;
var tpPlusFN = confusionMatrix[0].reduce(function(previousValue, currentValue, index, array) {
return previousValue + currentValue;
});
var truePositiveRate = confusionMatrix[0][0]/tpPlusFN
var fp = 0
var fpPlusTN = 0
for (var i=1;i<confusionMatrix.length;i++) {
fp += confusionMatrix[i][0]
fpPlusTN += confusionMatrix[i].reduce(function(previousValue, currentValue, index, array) {
return previousValue + currentValue;
});
}
var falsePositiveRate = fp / fpPlusTN;
if (evaluation.sampled_rows > settings.maxInstances) {
settings.maxInstances = evaluation.sampled_rows
}
result = {
"resource": evaluation.resource,
"tpr": truePositiveRate,
"fpr": falsePositiveRate,
"instances": evaluation.sampled_rows,
"auc": AUC(truePositiveRate, falsePositiveRate)}
settings.evaluations.push(result);
if (settings.evaluations.length < settings.urls.length) {
loadEvaluations(settings);
} else {
settings.callback(settings.evaluations, settings.maxInstances);
}
}
});
};
</script>
<script>
var margin = {top: 20, right: 60, bottom: 30, left: 60};
var width = 720 - margin.left - margin.right;
var height = 480 - margin.top - margin.bottom;
var xPadding = 20;
var yPadding = 35;
var xScale = d3.scale.linear()
.domain([0, 1])
.range([0, width]);
var yScale = d3.scale.linear()
.domain([0, 1])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(15);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(25);
var lineFunction = d3.svg.line()
.x(function(d) { return xScale(d.x); })
.y(function(d) { return yScale(d.y); })
.interpolate("linear");
var colorEvaluation = d3.scale.category20();
var colorLookup = function(evaluation) {
return colorEvaluation(evaluation.resource);
};
// SVG panel.
var svg = d3.select("#chart")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Adds X axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("False Positive Rate");
// Adds Y axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("True Positive Rate")
// Draw X-axis grid lines
svg.selectAll("line.x")
.data(xScale.ticks(10))
.enter().append("line")
.attr("class", "x")
.attr("x1", xScale)
.attr("x2", xScale)
.attr("y1", 0)
.attr("y2", height)
.style("stroke", "#ccc");
// Draw Y-axis grid lines
svg.selectAll("line.y")
.data(yScale.ticks(10))
.enter().append("line")
.attr("class", "y")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", yScale)
.attr("y2", yScale)
.style("stroke", "#ccc");
var isocost = 0.2
var prevalence = 0.5
var fpcost = 0.25
var fncost = 0.25
var costfp = (1 - prevalence) * fpcost
var costfn = prevalence * fncost
var slope = costfp / costfn;
var updateCostLines= function () {
costfp = (1 - prevalence) * fpcost;
costfn = Math.max(prevalence * fncost, 0.0001);
slope = costfp / costfn;
isoCostLines.attr("x1", function (cost) {
if (1 - cost/(costfn) < 0) {
return xScale(Math.min(1, (-1 + cost/(costfn))/slope))
} else {
return 0
}
})
.attr("x2", function (cost) {
if (slope + 1 - cost/(costfn) > 1) {
return xScale((1- (1 - cost/(costfn)))/slope)
} else {
return xScale(1)
}
})
.attr("y1", function (cost) {
return yScale(Math.max(0, 1 - cost/(costfn)));})
.attr("y2", function (cost) {
return yScale(Math.max(0, Math.min(1, slope + 1 - cost/(costfn))));})
}
// Draw Y-axis grid lines
var isoCostLines = svg.selectAll("iso-cost")
.data([isocost])
.enter().append("line")
.attr("class", "y")
.attr("x1", function (cost) {
if (1 - cost/(costfn) < 0) {
return xScale(Math.min(1, (-1 + cost/(costfn))/slope))
} else {
return 0
}
})
.attr("x2", function (cost) {
if (slope + 1 - cost/(costfn) > 1) {
return xScale((1- (1 - cost/(costfn)))/slope)
} else {
return xScale(1)
}
})
.attr("y1", function (cost) {
return yScale(Math.max(0, 1 - cost/(costfn)));})
.attr("y2", function (cost) {
return yScale(Math.max(0, Math.min(1, slope + 1 - cost/(costfn))));})
.style("stroke", "red");
d3.select("#isoline").on("change", function() {
isocost = this.value/100;
isoCostLines.data([isocost]);
updateCostLines();});
d3.select("#prevalence").on("change", function() {
prevalence = this.value/40;
$("#prevalencedisplay").html(prevalence);
updateCostLines();});
d3.select("#fpcost").on("change", function() {
fpcost = this.value/100;
$("#fpcostdisplay").html(fpcost);
updateCostLines();});
d3.select("#fncost").on("change", function() {
fncost = this.value/100;
$("#fncostdisplay").html(fncost);
updateCostLines();});
// Evaluation info
svg.append('text')
.attr("text-anchor", "middle")
.attr({'id': 'evaluationLabel', 'x': width/2, 'y': height-20})
.style({'font-size': '15px', 'font-weight': 'bold', 'fill': 'black'});
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) {
return xScale(d);})
.y(function(d) {
return yScale(d) });
var data = [0.2,0.2,0.4,0.4];
svg.append("svg:path").attr("d", line(data))
var path;
loadEvaluations({
callback: function(evaluations, maxInstances) {
// Draw evaluations
svg.selectAll("circle")
.data(evaluations)
.enter()
.append("circle")
.attr("cx", function(evaluation) {
return xScale(evaluation.fpr);
})
.attr("cy", function(evaluation) {
return yScale(evaluation.tpr);
})
.attr("r", function(evaluation) {
return (Math.sqrt(height - yScale(evaluation.instances/maxInstances)) + 5)
})
.attr("fill", colorLookup)
.attr("stroke", colorLookup)
.style('cursor', 'pointer')
.on('mouseover', function(evaluation) {
d3.select('svg #evaluationLabel')
.text(evaluation.resource + ", AUC: " + evaluation.auc.toFixed(2) + ", Instances: " + evaluation.instances)
.transition()
.style('opacity', 1);
auc = [{"x": evaluation.fpr, "y": evaluation.tpr},
{"x": 0, "y": 0},
{"x": 1, "y": 0},
{"x": 1, "y": 1},
{"x": evaluation.fpr, "y": evaluation.tpr}];
path = svg.append("path")
.attr("d", lineFunction(auc))
.attr("stroke", "blue")
.attr("stroke-width", 2)
.attr("fill", "yellow")
.attr("opacity", "0.25");})
.on('mouseout', function(d) {
d3.select('svg #evaluationLabel')
.transition()
.duration(1500)
.style('opacity', 0);
path.remove();});
// Draw legends
var legend = svg.selectAll(".legend")
.data(colorEvaluation.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width + 5)
.attr("width", 10)
.attr("height", 10)
.style("fill", colorEvaluation);
legend.append("text")
.attr("x", width - 40)
.attr("y", 4)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d });
}
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment