Skip to content

Instantly share code, notes, and snippets.

@sxywu
Last active June 28, 2016 01:51
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save sxywu/b2a192aced8b792075755abe715f8737 to your computer and use it in GitHub Desktop.
Chase Data: Stacked Bar Chart
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://npmcdn.com/babel-core@5.8.34/browser.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.js'></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<input id="loadfile" type="file" multiple />
<svg></svg>
<div id='data'></div>
<script type="text/babel">
/**************
* This section modified from @enjalot's block:
http://bl.ocks.org/enjalot/63d06e2ccadad0cb30dc5f920efd1cdf
***************/
d3.select("#loadfile").node()
.addEventListener('change', loadFile, false);
function loadFile(evt) {
evt.stopPropagation();
evt.preventDefault();
var files;
if(evt.dataTransfer) {
files = evt.dataTransfer.files;
} else {
files = evt.target.files;
}
var i = 0;
var reader = new FileReader();
reader.onload = function(e) {
parseData(e.target.result);
// start the next
i += 1;
files[i] && reader.readAsText(files[i]);
}
reader.readAsText(files[i]);
}
var allTransactions = [];
function parseData(result) {
var transactions = d3.csv.parse(result);
// go through each transaction
_.each(transactions, transaction => {
if (transaction['Type'] === 'Sale') {
allTransactions.push({
date: new Date(transaction['Trans Date']),
amount: -1 * parseFloat(transaction['Amount']),
title: transaction['Description'],
});
}
});
updateGraph(allTransactions);
}
// draw bar chart
var width = 900;
var height = 500;
var barWidth = 1;
var padding = {top: 40, left: 20};
var linearGradient = d3.select('svg').append('defs')
.append("linearGradient")
.attr("id", "linear-gradient")
.attr({gradientUnits: 'userSpaceOnUse',
x1: 0,
y1: 0,
x2: 0,
y2: height - padding.top});
var svg = d3.select('svg')
.attr({width, height})
.append('g');
var timeFormat = d3.time.format('%a %b %e');
// do da linear gradient
linearGradient.append("stop")
.attr("offset", 0)
.attr("stop-color", "#017351"); // start at red
/**
linearGradient.append("stop")
.attr("offset", '50%')
.attr("stop-color", "#03c383"); **/
linearGradient.append("stop")
.attr("offset", '100%')
.attr("stop-color", "#aad962"); // end at green
// initialize scales and axis
var colorScale = d3.scale.linear()
.range(["#aad962", "#03c383", "#017351"]);
var timeScale = d3.time.scale()
.range([0, width - padding.left]);
var xAxis = d3.svg.axis()
.orient('bottom')
.scale(timeScale);
var heightScale = d3.scale.linear()
.range([0, height - padding.top]);
var yAxis = d3.svg.axis()
.orient('left')
.scale(heightScale);
// initialize the containers
var barsG = svg.append('g')
.attr('transform', 'translate(' + padding.left + ',0)');
var xAxisG = svg.append('g')
.classed('axis', true)
.attr('transform',
'translate(' + padding.left + ',' + (height - padding.top) + ')');
var yAxisG = svg.append('g')
.classed('axis', true)
.attr('transform', 'translate(' + padding.left + ',0)');
function updateGraph(data) {
// first group the data into days
data = _.chain(data)
.groupBy(d => d.date)
.map((transactions) => {
return {
date: transactions[0].date,
transactions: _.sortBy(transactions, t => t.amount),
total: _.reduce(transactions, (sum, t) => {
return t.amount ? sum + t.amount : sum;
}, 0),
};
}).sortBy(d => d.date).value();
barWidth = Math.floor((width - padding.left) / data.length) - 2;
// update the scales
timeScale.domain([data[0].date, _.last(data).date]);
var maxTotal = d3.max(data, d => d.total);
heightScale.domain([0, maxTotal]);
colorScale.domain([0, maxTotal / 2, maxTotal]);
xAxisG.call(xAxis);
// yAxisG.call(yAxis);
var bars = barsG.selectAll('g')
.data(data, d => d.date);
bars.enter().append('g');
bars.exit().remove();
bars.attr('transform', d => {
return 'translate(' + timeScale(d.date) + ',' +
(height - padding.top - heightScale(d.total)) + ')';
});
var bar = bars.selectAll('rect')
.data(d => d.transactions);
bar.enter().append('rect');
bar.exit().remove();
var allY = 0;
bar.attr({
x: -barWidth / 2,
y: (d, i) => {
var y = allY;
allY += heightScale(d.amount);
if (!i) {
// if this is the first rect
y = 0;
allY = heightScale(d.amount);
}
return y;
},
width: barWidth,
height: d => heightScale(d.amount),
fill: d => colorScale(d.amount),
'fill-opacity': 0.5,
stroke: d => colorScale(d.amount),
cursor: 'pointer',
}).on('mouseover', d => {
var html = d.title + ' ';
html += '(' + timeFormat(d.date) + '): ';
html += '<b>$' + d.amount + '</b><br>';
d3.select('#data')
.html(html);
});
// move the group to the correct place
/**
bars
.attr({
width: barWidth,
height: d => heightScale(d.amount),
fill: d => colorScale(d.amount),
stroke: '#fff',
})
;
**/
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment