Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3.js - evenly spaced bars on a time scale

Tags:

I'm building a bar plot in d3.js in which each bar represents total TB cases during a month. The data essentially consists of a date (initially strings in %Y-%m format, but parsed using d3.time.format.parse) and an integer. I'd like the axis labels to be relatively flexible (show just year boundaries, label each month, etc.), but I'd also like the bars to be evenly spaced.

I can get flexible axis labeling when I use a date scale:

var xScaleDate = d3.time.scale()     .domain(d3.extent(thisstat, function(d) { return d.date; }))     .range([0, width - margin.left - margin.right]); 

... but the bars aren't evenly spaced due to varying numbers of days in each month (e.g., February and March are noticeably closer together than other months). I can get evenly-spaced bars using a linear scale:

var xScaleLinear = d3.scale.linear()       .domain([0, thisstat.length])       .range([0, width - margin.left - margin.right]); 

... but I can't figure out how to then have date-based axis labels. I've tried using both scales simultaneously and only generating an axis from the xScaleDate, just to see what would happen, but the scales naturally don't align quite right.

Is there a straightforward way to achieve this that I'm missing?

like image 401
Matt Parker Avatar asked Aug 29 '12 20:08

Matt Parker


1 Answers

You can combine ordinal and time scales:

// Use this to draw x axis var xScaleDate = d3.time.scale()     .domain(d3.extent(thisstat, function(d) { return d.date; }))     .range([0, width - margin.left - margin.right]);  // Add an ordinal scale var ordinalXScale = d3.scale.ordinal()     .domain(d3.map(thisstat, function(d) { return d.date; }))     .rangeBands([0, width], 0.4, 0);  // Now you can use both of them to space columns evenly: columnGroup.enter()     .append("rect")     .attr("class", "column")     .attr("width", ordinalXScale.rangeBand())     .attr("height", function (d) {         return height - yScale(d.value);     })     .attr("x", function (d) {         return xScaleDate(d.date);     })     .attr("y", function (d){         return yScale(d.value);     }); 

I've created an example a while ago to demonstrate this approach: http://codepen.io/coquin/pen/BNpQoO

like image 156
coquin Avatar answered Sep 30 '22 12:09

coquin