Skip to content

Instantly share code, notes, and snippets.

@aficionado
Last active December 24, 2015 06:09
Show Gist options
  • Save aficionado/6754933 to your computer and use it in GitHub Desktop.
Save aficionado/6754933 to your computer and use it in GitHub Desktop.
Visualizing Model Performance
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
position: relative;
width: 960px;
background: #fff;
}
text {
font: 10px sans-serif;
}
#chart {
position: absolute;
left: 10px;
top: 10px;
line-height: 150%
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
</style>
<body>
<div id="chart"></div>
</body>
<script src="http://code.jquery.com/jquery-1.7.1.js" type="text/javascript"></script>
<script src="http://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=16ca6a2ffcd0cf070781a6af08cabec2471aa68e',
'https://bigml.io/andromeda/evaluation/52407637035d0772e700ed71?username=francisco;api_key=16ca6a2ffcd0cf070781a6af08cabec2471aa68e',
'https://bigml.io/andromeda/evaluation/5230b787035d0772e3003ba6?username=francisco;api_key=16ca6a2ffcd0cf070781a6af08cabec2471aa68e',
'https://bigml.io/andromeda/evaluation/52274838035d0729c1000681?username=francisco;api_key=16ca6a2ffcd0cf070781a6af08cabec2471aa68e',
'https://bigml.io/andromeda/evaluation/51fb3e5e035d072bfd00111d?username=francisco;api_key=16ca6a2ffcd0cf070781a6af08cabec2471aa68e'
],
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");
// 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 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