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; | |
| } |