var barHeight = 25, barPad = 5, textDy = 16, width = 500; var margin = {top:0,bottom:0,left:80,right:0} d3.select("body") .style("position","fixed") var svg = d3.select(".chart").append("svg") .attr("width", width) .attr("margin", "10px") .style("position","fixed") ; var x = d3.scaleLinear() .range([0,width - margin.left]) .domain([0,(100)]); var colorArray = ['#66c2a5','#fc8d62','#8da0cb','#e78ac3','#a6d854','#ffd92f','#e5c494','#b3b3b3']; d3.csv("data.csv",types,function(error,data){ var keys = [], legKeys = []; _.keys(data[0]).forEach(function(d,i){ if (i > 0){ d.includes('pct') ? keys.push(d) : legKeys.push(d); } }); data = addTotal(data, legKeys); // create the legend $('.legend').css('margin-left', margin.left); legKeys.forEach(function(legKey,i){ $('.legend').append('
' + legKey); }); // sort the data, putting the total at the end var sorted = _.sortBy(data,keys[0]).reverse(), total = _.where(sorted,{group:'total'}), rem = _.reject(sorted,{group:'total'}), dat = []; rem.forEach(function(r){ dat.push(r); }); dat.push(total[0]); data = dat; // now that the data is ready, calculate the height var height = (data.length * barHeight) + (barPad * 3); d3.select('svg').attr("height", height); svg.selectAll('.label') .data(data) .enter().append('text') .attr('class', function(d){ var label; d.group == 'total' ? label = 'label strong' : label = 'label'; return label; }) .attr('x', margin.left-5) .attr('y', function(d,i){ return pad(d,i); }) .attr('dy', textDy) .attr('text-anchor', 'end') .text(function(d){ return d.group.charAt(0).toUpperCase() + d.group.slice(1); }); keys.forEach(function(key,i){ svg.selectAll('.bar .' + key) .data(data) .enter().append('rect') .attr('class', 'bar ' + key) .attr('width', function(d,i){ return x(d[key]); }) .attr('height', barHeight - barPad) .attr('x', function(d){ return widths(keys,key,d); }) .attr('y', function(d,i){ return pad(d,i); }); d3.selectAll('.' + key) .style('fill', colorArray[i]); svg.selectAll('.bar-label .' + key) .data(data) .enter().append('text') .attr('class', 'bar-label ' + key) .attr('x', function(d){ var barX; key !== keys[keys.length-1] ? barX = widths(keys,key,d) + 5 : barX = width - 5; return barX; }) .attr('dy', textDy) .attr('y', function(d,i){ return pad(d,i); }) .attr('text-anchor', function(d){ var ta; key == keys[keys.length-1] ? ta = 'end' : ta = 'start'; return ta; }) .text(function(d){ var text; d[key] < 5 ? text = '' : text = Math.round(d[key]) + pct(d, data[0].group, data[data.length-1].group); return text; }); }); }); function types(d){ var keys = _.keys(d); var arr = []; keys.forEach(function(k,i){ if (i!==0){ arr.push(+d[k]); d[k] = +d[k]; } }); var sum = arr.reduce(function(a,b){return a+b}, 0); keys.forEach(function(k,i){ i !== 0 ? d[k + 'pct'] = +d[k]/sum*100 : null; }); return d; } function pad(d,i){ var barI; d.group == 'total' ? barI = i * barHeight + (barPad * 2 + 2) : barI = i * barHeight; return barI; } function pct(d, first, last){ var extra; d.group == first || d.group == last ? extra = '%' : extra = ''; return extra; } function widths(keys,key,d){ if (key == keys[0]) { return margin.left + 5; } else if (key == keys[1]){ return margin.left + x(d[keys[0]]); } else if (key == keys[2]){ return margin.left + x(d[keys[0]]) + x(d[keys[1]]); } else if (key == keys[3]) { return margin.left + x(d[keys[0]]) + x(d[keys[1]]) + x(d[keys[2]]); } else if (key == keys[4]) { return margin.left + x(d[keys[0]]) + x(d[keys[1]]) + x(d[keys[2]]) + x(d[keys[3]]); } else if (key == keys[5]) { return margin.left + x(d[keys[0]]) + x(d[keys[1]]) + x(d[keys[2]]) + x(d[keys[3]]) + x(d[keys[4]]); } else if (key == keys[6]) { return margin.left + x(d[keys[0]]) + x(d[keys[1]]) + x(d[keys[2]]) + x(d[keys[3]]) + x(d[keys[4]]) + x(d[keys[5]]); } else if (key == keys[7]) { return margin.left + x(d[keys[0]]) + x(d[keys[1]]) + x(d[keys[2]]) + x(d[keys[3]]) + x(d[keys[4]]) + x(d[keys[5]]) + x(d[keys[6]]); } } function addTotal(data,keys){ var totObj = {}; var arrA = []; keys.forEach(function(key){ var arrB = []; data.forEach(function(d){ arrB.push(d[key]); }) var sumB = arrB.reduce(function(a,b){ return a+b }, 0); totObj[key] = sumB; arrA.push(sumB); }) var sumA = arrA.reduce(function(a,b){ return a+b }, 0); keys.forEach(function(key){ totObj[key + 'pct'] = totObj[key] / sumA * 100; }); totObj.group = 'total'; data.push(totObj); return data; }