Expenses in red below, income in green above, over time on the x axis, value on y axis.
You can see this chart at: http://bl.ocks.org/galvanic/284b254c6fe3a501d8bcc46c95e6f601
Expenses in red below, income in green above, over time on the x axis, value on y axis.
You can see this chart at: http://bl.ocks.org/galvanic/284b254c6fe3a501d8bcc46c95e6f601
| function TimelineChart() { | |
| // assumptions about the data passed to this chart: | |
| // i.e. dataset of each selection | |
| // list of objects. each object has the following keys: | |
| // chart config | |
| var margin = {top: 20, right: 20, bottom: 20, left: 20} | |
| var width = 960 - margin.left - margin.right | |
| var height = 500 - margin.top - margin.bottom | |
| var chartMiddle = height/2 | |
| var offsetFromCentralLine = 4 | |
| var xScale = d3.time.scale() | |
| .range([0, width]) | |
| var yScale = d3.scale.log() | |
| .range([0, chartMiddle]) | |
| function drawChart(selection) { | |
| selection.each(function(dataset, i) { | |
| var wholeChart = this | |
| // update scale domain using dataset metadata | |
| xScale | |
| .domain(d3.extent(dataset, function(d) { return d.date })) | |
| .nice(d3.time.month) | |
| // extent top and bottom must be same for consistency | |
| // so look for the maximum total over the whole period ! | |
| var maxTotal = d3.max(dataset, function(d) { return Math.max(Math.abs(d.values.totalNeg), d.values.totalPos) }) | |
| yScale | |
| .domain([0.1, maxTotal]) | |
| .nice() | |
| // | |
| // Y AXIS | |
| // | |
| // the constructor function for the grid | |
| var yGrid = d3.svg.axis() | |
| .scale(yScale) | |
| .orient('left') | |
| .ticks(1, ',.1') | |
| .innerTickSize(-width) | |
| .outerTickSize(0) | |
| .tickPadding(-width+5) | |
| // the svg items making up the grid | |
| var yGridLines = d3.select(wholeChart) | |
| .append('g') | |
| .classed('y grid', true) | |
| .attr('transform', 'translate(' + margin.left + ',' + (margin.top + chartMiddle + offsetFromCentralLine) + ')') | |
| .call(yGrid) | |
| yGridLines.selectAll('text') | |
| .attr('dy', 0) | |
| .attr('alignment-baseline', 'baseline') | |
| // the svg items making up the grid | |
| var yGridLines = d3.select(wholeChart) | |
| .append('g') | |
| .classed('y grid', true) | |
| .attr('transform', 'translate(' + margin.left + ',' + (margin.top - offsetFromCentralLine) + ')') | |
| .call(yGrid.scale(yScale.copy().range([chartMiddle, 0]))) | |
| yGridLines.selectAll('text') | |
| .attr('dy', 0) | |
| .attr('alignment-baseline', 'mathematical') | |
| // | |
| // THE CHART | |
| // | |
| // select chart element(s) (eg. innerchart) if they exist already | |
| var innerchart = d3.select(wholeChart).selectAll('g.innerchart') | |
| .data(function(d) { return [d] }) | |
| // otherwise create them and the elements they contain | |
| var innerchartEnter = innerchart.enter() | |
| .append('g') | |
| .classed('innerchart', true) | |
| .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') | |
| innerchartEnter.append('line') | |
| .attr('x1', 0) | |
| .attr('y1', chartMiddle) | |
| .attr('x2', width) | |
| .attr('y2', chartMiddle) | |
| // update data | |
| var days = innerchart.selectAll('g.day') | |
| .data(function(d) { return d }, function(d) { return d.key }) | |
| days.enter() | |
| .append('g') | |
| .classed('day', true) | |
| .attr('transform', function(d) { return 'translate(' + xScale(d.date) + ',' + chartMiddle + ')' }) | |
| var negativeTransactions = days.selectAll('rect.transaction.negative') | |
| .data(function(d) { return [d.values.totalNeg] }) | |
| negativeTransactions.enter() | |
| .append('rect') | |
| .classed('transaction', true) | |
| .classed('negative', true) | |
| .attr('x', 0) | |
| .attr('y', offsetFromCentralLine) | |
| .attr('width', 2) | |
| .attr('height', 0) | |
| .transition() | |
| .delay(function(d) { return days.data().indexOf(d3.select(this.parentNode).datum()) * 50 }) | |
| .duration(1000) | |
| .ease('exp') | |
| .attr('height', function(d) { return (d == 0 ? 0 : yScale(-d)) }) | |
| var positiveTransactions = days.selectAll('rect.transaction.positive') | |
| .data(function(d) { return [d.values.totalPos] }) | |
| positiveTransactions.enter() | |
| .append('rect') | |
| .classed('transaction', true) | |
| .classed('positive', true) | |
| .attr('x', 0) | |
| .attr('y', -offsetFromCentralLine) | |
| .attr('width', 2) | |
| .attr('height', 0) | |
| .transition() | |
| .delay(function(d) { return days.data().indexOf(d3.select(this.parentNode).datum()) * 50 }) | |
| .duration(1000) | |
| .ease('exp') | |
| .attr('height', function(d) { return (d == 0 ? 0 : yScale(d)) }) | |
| .attr('y', function(d) { return (d == 0 ? 0 : -yScale(d) -offsetFromCentralLine) }) | |
| }) | |
| } | |
| return drawChart | |
| } |
| date | value | |
|---|---|---|
| 10/10/2011 | -20.00 | |
| 10/10/2011 | -39.84 | |
| 10/10/2011 | -20.00 | |
| 11/10/2011 | -3.25 | |
| 13/10/2011 | -10.00 | |
| 13/10/2011 | -10.00 | |
| 17/10/2011 | -20.00 | |
| 17/10/2011 | -12.40 | |
| 17/10/2011 | -6.99 | |
| 17/10/2011 | -20.00 | |
| 20/10/2011 | -15.00 | |
| 20/10/2011 | -10.00 | |
| 24/10/2011 | -496.20 | |
| 24/10/2011 | -12.40 | |
| 24/10/2011 | -7.00 | |
| 24/10/2011 | -5.19 | |
| 27/10/2011 | -5.00 | |
| 27/10/2011 | -10.93 | |
| 27/10/2011 | -15.00 | |
| 31/10/2011 | -5.00 | |
| 31/10/2011 | -20.00 | |
| 31/10/2011 | -5.57 | |
| 04/11/2011 | -5.00 | |
| 04/11/2011 | -12.40 | |
| 07/11/2011 | -2.20 | |
| 07/11/2011 | -61.89 | |
| 07/11/2011 | -9.78 | |
| 07/11/2011 | -5.25 | |
| 08/11/2011 | -15.51 | |
| 08/11/2011 | -54.85 | |
| 09/11/2011 | -10.00 | |
| 09/11/2011 | -30.00 | |
| 11/11/2011 | -10.58 | |
| 11/11/2011 | -12.40 | |
| 14/11/2011 | -17.00 | |
| 14/11/2011 | -68.00 | |
| 16/11/2011 | -496.20 | |
| 16/11/2011 | -20.00 | |
| 18/11/2011 | 32.00 | |
| 18/11/2011 | 20.00 | |
| 18/11/2011 | -11.00 | |
| 21/11/2011 | -6.00 | |
| 21/11/2011 | -6.51 | |
| 21/11/2011 | -10.54 | |
| 21/11/2011 | -38.50 | |
| 22/11/2011 | -12.40 | |
| 25/11/2011 | -5.15 | |
| 28/11/2011 | -10.00 | |
| 28/11/2011 | -10.93 | |
| 28/11/2011 | -10.00 | |
| 28/11/2011 | -20.00 | |
| 30/11/2011 | -5.47 | |
| 30/11/2011 | -10.00 | |
| 02/12/2011 | -5.00 | |
| 05/12/2011 | -79.99 | |
| 05/12/2011 | -10.00 | |
| 05/12/2011 | -24.80 | |
| 05/12/2011 | -34.20 | |
| 08/12/2011 | -2.50 | |
| 09/12/2011 | -4.50 | |
| 09/12/2011 | -7.00 | |
| 12/12/2011 | -10.00 | |
| 12/12/2011 | -4.99 | |
| 12/12/2011 | -19.50 | |
| 12/12/2011 | -112.00 | |
| 15/12/2011 | -12.50 | |
| 15/12/2011 | -496.20 | |
| 19/12/2011 | -31.25 | |
| 19/12/2011 | -4.18 | |
| 19/12/2011 | -36.25 | |
| 19/12/2011 | -0.69 | |
| 19/12/2011 | -5.00 | |
| 28/12/2011 | -10.93 | |
| 30/12/2011 | -11.25 | |
| 03/01/2012 | -7.00 | |
| 09/01/2012 | -15.00 | |
| 09/01/2012 | -10.00 | |
| 09/01/2012 | -13.10 | |
| 11/01/2012 | -26.00 | |
| 16/01/2012 | -10.00 | |
| 18/01/2012 | -17.89 | |
| 19/01/2012 | 603.00 | |
| 19/01/2012 | 35.00 | |
| 19/01/2012 | -50.00 | |
| 19/01/2012 | -38.50 | |
| 23/01/2012 | -4.99 | |
| 24/01/2012 | -10.00 | |
| 25/01/2012 | -2.09 | |
| 26/01/2012 | -30.98 | |
| 26/01/2012 | -10.00 | |
| 27/01/2012 | -10.93 | |
| 30/01/2012 | 140.00 | |
| 30/01/2012 | -629.46 | |
| 30/01/2012 | -12.60 | |
| 02/02/2012 | -5.79 | |
| 06/02/2012 | -6.71 | |
| 07/02/2012 | -15.00 | |
| 08/02/2012 | -12.00 | |
| 13/02/2012 | 150.00 | |
| 13/02/2012 | -19.99 | |
| 14/02/2012 | -41.05 | |
| 17/02/2012 | -7.40 | |
| 17/02/2012 | -10.00 | |
| 17/02/2012 | -5.20 | |
| 23/02/2012 | 600.00 | |
| 23/02/2012 | -38.50 | |
| 23/02/2012 | -10.96 | |
| 23/02/2012 | -20.00 | |
| 27/02/2012 | -506.00 | |
| 01/03/2012 | -5.80 | |
| 01/03/2012 | -20.00 | |
| 05/03/2012 | 160.00 | |
| 05/03/2012 | -140.00 | |
| 05/03/2012 | -10.00 | |
| 05/03/2012 | -4.99 | |
| 06/03/2012 | 82.00 | |
| 07/03/2012 | -50.00 | |
| 12/03/2012 | -10.00 | |
| 12/03/2012 | -20.00 | |
| 13/03/2012 | -5.00 | |
| 15/03/2012 | -10.00 | |
| 15/03/2012 | -2.90 | |
| 19/03/2012 | 600.00 | |
| 19/03/2012 | -516.20 | |
| 19/03/2012 | -12.50 | |
| 19/03/2012 | -15.00 | |
| 19/03/2012 | -2.65 | |
| 19/03/2012 | -20.00 | |
| 21/03/2012 | -20.00 | |
| 22/03/2012 | -10.00 | |
| 23/03/2012 | -9.49 | |
| 23/03/2012 | -24.86 | |
| 23/03/2012 | -8.00 | |
| 17/04/2012 | 500.00 | |
| 17/04/2012 | -496.20 | |
| 26/04/2012 | -15.00 | |
| 27/04/2012 | -6.67 | |
| 27/04/2012 | -46.00 | |
| 30/04/2012 | 180.00 | |
| 30/04/2012 | -9.16 | |
| 30/04/2012 | -3.50 | |
| 30/04/2012 | -5.11 | |
| 02/05/2012 | -3.74 | |
| 03/05/2012 | -29.32 | |
| 03/05/2012 | -14.99 | |
| 03/05/2012 | -18.00 | |
| 03/05/2012 | -3.50 | |
| 04/05/2012 | -20.00 | |
| 08/05/2012 | -4.49 | |
| 08/05/2012 | -30.00 | |
| 10/05/2012 | -3.25 | |
| 14/05/2012 | 160.00 | |
| 14/05/2012 | -20.00 | |
| 14/05/2012 | -52.78 | |
| 14/05/2012 | -30.00 | |
| 17/05/2012 | -3.43 | |
| 17/05/2012 | -5.13 | |
| 21/05/2012 | -6.75 | |
| 21/05/2012 | -20.00 | |
| 21/05/2012 | -10.00 | |
| 24/05/2012 | -6.00 | |
| 24/05/2012 | -5.50 | |
| 25/05/2012 | -8.00 | |
| 25/05/2012 | -15.00 | |
| 28/05/2012 | 180.00 | |
| 28/05/2012 | -13.99 | |
| 28/05/2012 | -10.00 | |
| 28/05/2012 | -10.00 | |
| 28/05/2012 | -4.99 | |
| 01/06/2012 | -1.50 | |
| 06/06/2012 | 500.00 | |
| 06/06/2012 | -496.20 | |
| 06/06/2012 | -8.98 | |
| 07/06/2012 | -66.70 | |
| 07/06/2012 | -3.50 | |
| 11/06/2012 | -10.00 | |
| 11/06/2012 | -10.50 | |
| 13/06/2012 | -10.00 | |
| 18/06/2012 | 500.00 | |
| 18/06/2012 | 130.00 | |
| 18/06/2012 | -496.20 | |
| 18/06/2012 | -51.50 | |
| 19/06/2012 | -5.00 | |
| 19/06/2012 | -6.30 | |
| 19/06/2012 | -20.00 | |
| 22/06/2012 | -20.00 | |
| 25/06/2012 | -48.85 | |
| 26/06/2012 | -10.00 | |
| 27/06/2012 | -23.74 | |
| 29/06/2012 | 200.00 | |
| 29/06/2012 | -5.34 | |
| 29/06/2012 | -10.00 | |
| 29/06/2012 | -3.50 | |
| 02/07/2012 | -3.50 | |
| 02/07/2012 | -7.50 | |
| 05/07/2012 | -3.50 | |
| 12/07/2012 | -10.00 | |
| 12/07/2012 | -20.00 | |
| 16/07/2012 | 700.00 | |
| 16/07/2012 | -496.20 | |
| 16/07/2012 | -187.00 | |
| 24/07/2012 | -67.99 | |
| 27/07/2012 | 345.00 | |
| 30/07/2012 | -3.21 | |
| 20/08/2012 | 300.00 | |
| 20/08/2012 | -23.61 | |
| 20/08/2012 | -496.20 | |
| 28/08/2012 | -9.06 | |
| 31/08/2012 | 575.00 | |
| 11/09/2012 | -90.00 | |
| 03/10/2012 | -4.00 | |
| 03/01/2012 | 0.30 | |
| 05/01/2012 | 2073.50 | |
| 19/01/2012 | -603.00 | |
| 30/01/2012 | -140.00 | |
| 13/02/2012 | -150.00 | |
| 23/02/2012 | -600.00 | |
| 05/03/2012 | -160.00 | |
| 19/03/2012 | -600.00 | |
| 02/04/2012 | 0.48 | |
| 17/04/2012 | -500.00 | |
| 27/04/2012 | 2852.50 | |
| 30/04/2012 | -180.00 | |
| 14/05/2012 | -160.00 | |
| 28/05/2012 | -180.00 | |
| 06/06/2012 | -500.00 | |
| 18/06/2012 | -500.00 | |
| 18/06/2012 | -130.00 | |
| 29/06/2012 | -200.00 | |
| 02/07/2012 | 0.49 | |
| 16/07/2012 | -700.00 | |
| 20/08/2012 | -300.00 | |
| 03/09/2012 | -199.68 | |
| 01/10/2012 | 0.22 | |
| 01/10/2012 | -148.87 |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Visualisation</title> | |
| <link rel='stylesheet' type='text/css' href='style.css'> | |
| <script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js' charset='utf-8'></script> | |
| <script src='https://cdnjs.cloudflare.com/ajax/libs/chance/1.0.3/chance.min.js' charset='utf-8'></script> | |
| <script src='./chart.js'></script> | |
| <script src='./main.js'></script> | |
| </head> | |
| <body> | |
| <div id='vis'></div> | |
| </body> | |
| </html> |
| 'use strict' | |
| var dataFilepath = 'data.csv' | |
| d3.csv(dataFilepath, cleanCSVdata, drawChart) | |
| //generateData(200, Math.random, drawChart) | |
| function generateData(numDatapoints, seed, callback) { | |
| // GENERATE FAKE DATASET | |
| var chance = new Chance(seed) | |
| var dataset = new Array(numDatapoints) | |
| for (var i = 0; i < numDatapoints; i++) { | |
| dataset[i] = { | |
| date: d3.time.format('%d/%m/%Y').parse(chance.date({string: true, american: false, year: chance.integer({min: 2011, max:2014})})) | |
| , value: chance.floating({min: -1000, max: 1000, fixed: 2}) | |
| } | |
| } | |
| document.addEventListener('DOMContentLoaded', function(event) { | |
| callback(dataset) | |
| }) | |
| } | |
| function cleanCSVdata(d) { | |
| return { | |
| date: d3.time.format('%d/%m/%Y').parse(d.date) | |
| , value: +d.value | |
| } | |
| } | |
| function drawChart(dataset) { | |
| // PREPARE DATA FOR CHART | |
| // sort by date, order is kept during nesting step | |
| dataset.sort(function(a, b) { return a.date - b.date }) | |
| var balanceByDay = d3.nest() | |
| .key(function(d) { return d.date.toDateString() }) | |
| .rollup(function(values) { | |
| // get the highest number out of the sum of the positive numbers | |
| // and the sum of the negative numbers | |
| var negativeTransactions = values.filter(function(d) { return d.value < 0 }) | |
| var positiveTransactions = values.filter(function(d) { return d.value > 0 }) | |
| return { | |
| transactions: values | |
| , negTransactions: negativeTransactions | |
| , posTransactions: positiveTransactions | |
| , totalNeg: d3.sum(negativeTransactions.map(function(d) { return d.value })) | |
| , totalPos: d3.sum(positiveTransactions.map(function(d) { return d.value })) | |
| } | |
| }) | |
| .entries(dataset) | |
| balanceByDay.forEach(function(d) { d.date = d.values.transactions[0].date }) | |
| // DRAW THE CHART | |
| var svg = d3.select('div#vis') | |
| .append('svg') | |
| .attr('width', 960) | |
| .attr('height', 500) | |
| function draw(dataset) { | |
| // draw chart | |
| var timelines = svg.selectAll('g.timelinechart') | |
| .data(dataset) | |
| timelines.enter() | |
| .append('g') | |
| .classed('timelinechart', true) | |
| timelines | |
| .call(TimelineChart()) | |
| timelines.exit() | |
| .remove() | |
| } | |
| draw([balanceByDay]) | |
| } | |
| div#vis { | |
| width: 960px; | |
| margin: 0px auto; | |
| } | |
| g.innerchart > line { | |
| stroke: orange; | |
| } | |
| rect.transaction { | |
| fill: grey; | |
| } | |
| rect.transaction.negative { | |
| fill: red; | |
| } | |
| rect.transaction.positive { | |
| fill: green; | |
| } | |
| .grid { | |
| opacity: 0.3; | |
| } | |
| .grid .tick { | |
| stroke: lightgrey; | |
| } | |
| .grid text { | |
| stroke: black; | |
| font-family: monospace; | |
| font-size: 0.9em; | |
| } | |
| .grid path { | |
| stroke-width: 0; | |
| } | |
| .grid .tick:first-child { | |
| display: none; | |
| } |