d3js: Allocation, return, and attribution example. Sortable, horizontal bar chart with positive and negative values that change on select input.
http://bl.ocks.org/mbostock/5977197 http://bl.ocks.org/mbostock/3885705
d3js: Allocation, return, and attribution example. Sortable, horizontal bar chart with positive and negative values that change on select input.
http://bl.ocks.org/mbostock/5977197 http://bl.ocks.org/mbostock/3885705
{ | |
"keys" : [ | |
"variable", | |
"date", | |
"allocation", | |
"return", | |
"attribution" | |
], | |
"values" : [ | |
{ | |
"variable" : "Convertible Arbitrage", | |
"date" : 14487, | |
"allocation" : 0.12905, | |
"return" : 0.0315, | |
"attribution" : 0.004065 | |
}, | |
{ | |
"variable" : "CTA Global", | |
"date" : 14487, | |
"allocation" : 0.12462, | |
"return" : 0.0054, | |
"attribution" : 0.00067293 | |
}, | |
{ | |
"variable" : "Distressed Securities", | |
"date" : 14487, | |
"allocation" : 0.0070071, | |
"return" : 0.0244, | |
"attribution" : 0.00017097 | |
}, | |
{ | |
"variable" : "Emerging Markets", | |
"date" : 14487, | |
"allocation" : 0.013153, | |
"return" : 0.0166, | |
"attribution" : 0.00021834 | |
}, | |
{ | |
"variable" : "Equity Market Neutral", | |
"date" : 14487, | |
"allocation" : 0.013939, | |
"return" : 0.007, | |
"attribution" : 9.7575e-05 | |
}, | |
{ | |
"variable" : "Event Driven", | |
"date" : 14487, | |
"allocation" : 0.04947, | |
"return" : 0.0207, | |
"attribution" : 0.001024 | |
}, | |
{ | |
"variable" : "Fixed Income Arbitrage", | |
"date" : 14487, | |
"allocation" : 0.083955, | |
"return" : 0.0202, | |
"attribution" : 0.0016959 | |
}, | |
{ | |
"variable" : "Global Macro", | |
"date" : 14487, | |
"allocation" : 0.017134, | |
"return" : 0.005, | |
"attribution" : 8.5669e-05 | |
}, | |
{ | |
"variable" : "Long Short Equity", | |
"date" : 14487, | |
"allocation" : 0.063293, | |
"return" : 0.0157, | |
"attribution" : 0.00099371 | |
}, | |
{ | |
"variable" : "Merger Arbitrage", | |
"date" : 14487, | |
"allocation" : 0.16361, | |
"return" : 0.0102, | |
"attribution" : 0.0016688 | |
}, | |
{ | |
"variable" : "Relative Value", | |
"date" : 14487, | |
"allocation" : 0.11604, | |
"return" : 0.0162, | |
"attribution" : 0.0018799 | |
}, | |
{ | |
"variable" : "Short Selling", | |
"date" : 14487, | |
"allocation" : 0.010975, | |
"return" : -0.0165, | |
"attribution" : -0.00018109 | |
}, | |
{ | |
"variable" : "Funds of Funds", | |
"date" : 14487, | |
"allocation" : 0.20776, | |
"return" : 0.0113, | |
"attribution" : 0.0023476 | |
} | |
] | |
} |
<!DOCTYPE html> | |
<meta charset='utf-8'> | |
<html> | |
<head> | |
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
<link rel='stylesheet' href='style.css'> | |
</head> | |
<body> | |
<script type='text/javascript' src='script.js'></script> | |
</body> | |
</html> |
//http://bl.ocks.org/mbostock/5977197 | |
//http://bl.ocks.org/mbostock/3885705 | |
var selectDiv = d3.select('body').append('div').attr('class','selectDiv') | |
var margin = {top: 20, right: 20, bottom: 30, left: 150}, | |
width = 900 - margin.left - margin.right, | |
height = 600 - margin.top - margin.bottom; | |
var formatPercent = d3.format(".0%"); | |
var formatPercent2 = d3.format(".2%"); | |
var svg = d3.select("body").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 + ")"); | |
var dataset = [] // able to look at dataset in console | |
d3.json('data.json',function (error,data) { | |
data.values.forEach(function (d) { | |
d.variable = d.variable.toString() | |
d.allocation = +d.allocation | |
d.return = +d.return | |
d.attribution = +d.attribution | |
}) | |
data.keys.shift() // remove keys.date | |
data.keys.shift() // remove keys.variable | |
dataset = data // able to look at dataset in console | |
d3.select('.selectDiv') | |
.append('select') | |
.attr('id','selectVariable') | |
.on('change',change) | |
.selectAll('option') | |
.data(data.keys).enter() | |
.append('option') | |
.attr('value',function (d) { return d; }) | |
.text(function (d) { return d;}) | |
var selectVariableValue = d3.select('#selectVariable').property('value') | |
d3.select('.selectDiv') | |
.append('label') | |
.text('Sort?') | |
.append('input') | |
.attr('id','sortCheckbox') | |
.attr('type','checkbox') | |
.attr('value','sort') | |
.attr('name','sort') | |
.on('change',sortBars) | |
var yValue = function(d) { return d['variable']; }, // data -> value | |
yScale = d3.scale.ordinal().rangeRoundBands([0, height], 0.1), // value -> display | |
yMap = function(d) { return yScale(yValue(d)); }, // data -> display | |
yAxis = d3.svg.axis().scale(yScale).orient("left"); | |
var xValue = function(d) { return d[selectVariableValue]; }, // data -> value | |
xScale = d3.scale.linear().range([0,width]), // value -> display | |
xMap = function(d) { return xScale(xValue(d)); }, // data -> display | |
xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickFormat(formatPercent); | |
var xScaleMax = d3.max([ | |
d3.max([d3.max(data.values,xValue),0]), // max positive value | |
Math.abs(d3.min([d3.min(data.values,xValue),0])) // min negative value | |
]) | |
yScale.domain(data.values.map(yValue)); | |
xScale.domain([0,d3.max(data.values,xValue)]).nice() | |
// Create axes | |
svg.append("g") | |
.attr("class", "axis") | |
.attr('id','xAxis') | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis) | |
svg.append("g") | |
.attr("class", "axis") | |
.attr('id','yAxis') | |
.call(yAxis) | |
// Create bars | |
var bars = svg.selectAll(".bar") | |
.data(data.values) | |
bars.enter() | |
.append("rect") | |
.attr("class", function (d) { return ( d[selectVariableValue] > 0 ) | |
? 'barPos' | |
: 'barNeg'}) | |
.attr('x', function (d) { return xScale(Math.min(0,d[selectVariableValue]));}) | |
.attr("y", yMap) | |
.attr("width", function (d) { return Math.abs(xScale(d[selectVariableValue]) - xScale(0));}) | |
.attr("height", yScale.rangeBand()) | |
bars.append('title') | |
.text(function (d) { return d['variable'] + '\n' + selectVariableValue + ": " + formatPercent2(d[selectVariableValue]); }) | |
function change() { | |
var value = this.value // get new dataset value | |
d3.select('#sortCheckbox').property('checked',false) | |
if(value==='attribution') { | |
var formatPercent = d3.format(".2%") | |
} else { | |
var formatPercent = d3.format(".0%") | |
} | |
var xValue = function(d) { return d[value]; }, // data -> value | |
xScale = d3.scale.linear().range([0,width]), // value -> display | |
xMap = function(d) { return xScale(xValue(d)); }, // data -> display | |
xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickFormat(formatPercent); | |
var xScaleMax = d3.max([ | |
d3.max([d3.max(data.values,xValue),0]), // max positive value | |
Math.abs(d3.min([d3.min(data.values,xValue),0])) // min negative value | |
]) | |
value==='allocation' | |
? xScale.domain([0,d3.max(data.values,xValue)]).nice() | |
: xScale.domain([-xScaleMax,xScaleMax]).nice() | |
bars.selectAll('title') | |
.text(function (d) { return d['variable'] + '\n' + value + ": " + formatPercent2(d[value]); }) | |
bars.transition().duration(1000) // redraw the bars | |
.attr("class", function (d) { return d[value] > 0 ? 'barPos':'barNeg'}) | |
.attr('x', function (d) { return xScale(Math.min(0,d[value]));}) | |
.attr("y", yMap) | |
.attr("width", function (d) { return Math.abs(xScale(d[value]) - xScale(0));}) | |
.attr("height", yScale.rangeBand()) | |
d3.select('#xAxis') // redraw the x axis | |
.transition().duration(1000) | |
.call(xAxis) | |
} | |
function sortBars() { | |
var value = d3.select('select').property('value') | |
var y0 = yScale.domain(data.values.sort(this.checked | |
? function (a,b) { return b[value] - a[value]; } | |
: function (a,b) { return d3.ascending(a['variable'],b['variable']); }) | |
.map(function (d) { return d['variable']; })) | |
.copy() | |
var transition = svg.transition().duration(1000), | |
delay = function (d,i) { return i*50 } | |
transition.selectAll('rect') | |
.delay(delay) | |
.attr('y', function (d) { return y0(d.variable)}) | |
transition.select('#yAxis') | |
.call(yAxis) | |
.selectAll('g') | |
.delay(delay) | |
} | |
}) |
body { | |
font: 10px sans-serif; | |
} | |
label { | |
font: 14px sans-serif; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
.barPos { | |
fill: steelblue; | |
} | |
.barNeg { | |
fill: brown; | |
} | |
.barPos:hover, | |
.barNeg:hover { | |
fill: orange; | |
} | |
.values { | |
fill: black; | |
} | |
.variableLabels { | |
fill: black; | |
text-anchor: start; | |
font-size: 14px; | |
} |