Help creating a bar chart with continuous time axis?

8,628 views
Skip to first unread message

Nate Agrin

unread,
Apr 7, 2012, 11:54:50 PM4/7/12
to d3...@googlegroups.com
Does anyone have a good example of creating a bar chart with a time based x-axis? Ideally it would look something like the Crossfilter chart rendered using rect elements, as opposed to writing the bars by hand with path elements.

Basically I want something like the ordinal rangeBands method for the time scale, while preserving the ability to pass the time scale to an axis for pretty formatting purposes. I suspect there is some simple way to do this but I've yet to figure it out or find an example.

Many thanks in advanced. 
Nate

Kai Chang

unread,
Apr 7, 2012, 11:57:36 PM4/7/12
to d3...@googlegroups.com
Rickshaw might work for what you need: http://code.shutterstock.com/rickshaw/

Mike Bostock

unread,
Apr 8, 2012, 1:26:11 AM4/8/12
to d3...@googlegroups.com
> Basically I want something like the ordinal rangeBands method for the time
> scale, while preserving the ability to pass the time scale to an axis for
> pretty formatting purposes.

If you want to use the ordinal scale's rangeBands, then use a time
interval to generate an array of all the times you want to display.
For example, if you want to display one rect per day, you might say:

var x = d3.scale.ordinal()
.domain(d3.time.days(new Date(2011, 0, 1), new Date(2011, 3, 1)))
.rangeRoundBands([0, width], .1);

If you want to generate pretty labels for this, you can use
d3.svg.axis. You can generate a d3.time.format and pass that to
axis.tickFormat.

Mike

Nate Agrin

unread,
Apr 8, 2012, 4:19:38 AM4/8/12
to d3...@googlegroups.com
That's not going to help.

Nate Agrin

unread,
Apr 8, 2012, 4:20:38 AM4/8/12
to d3...@googlegroups.com
Thanks Mike. I'll give this a shot and see how it works out.

Nate Agrin

unread,
Apr 8, 2012, 2:24:06 PM4/8/12
to d3...@googlegroups.com
Mike,

I'm kind of back where I started with this problem. The solution you
suggested works, but the problem I'm facing with label overplotting
still exists. It seems like I have two options:

1. Use a d3.time.scale instance and then calculate my own range band
widths. This would give me the pretty axis labels and eliminate
overplotting. A disadvantage would be the labels wouldn't line up with
the bars themselves, but with the space between bars, which I'm
alright with.

2. Hack together an ordinal scale with a tick function that implements
a #ticks method so that it behaves a bit more like the quantitative
scales.

Do you have any thoughts? In the crossfilter example you manually draw
bars with a path element and a specific function for rendering the
path. If I can, I'd like to avoid that so I can attach event handlers
to each bar via rect elements.

Thanks again,
Nate

Rob Dodson

unread,
May 2, 2012, 7:57:14 PM5/2/12
to d3...@googlegroups.com
I think I'm in a similar boat:

http://jsfiddle.net/robdodson/KWRxW/

Mike Bostock

unread,
May 2, 2012, 8:14:32 PM5/2/12
to d3...@googlegroups.com
Apologies for the delayed response, but since this thread got bumped,
here's a suggestion.

> 1. Use a d3.time.scale instance and then calculate my own range band

Yep, that's the technique we used for Square's merchant analytics and
the Crossfilter demo. For the Crossfilter demo, I was lazy and just
left the default axis ticks as-is, which is why the day labels are
somewhat misleadingly placed in the whitespace between bars. For
merchant analytics, we did the right thing: we select the tick labels
after creating the axis and fix the position.

In general, D3's components are designed to support configuration as
well as post-processing. For the common cases, you can set the
appropriate configuration property (e.g., the axis orientation). For
custom control, you can select the elements created by the component
and modify them as desired. You can also remove or add new elements.

So, I'd recommend using a d3.time.scale here, if the x-axis is time.
That seems to be a closer conceptual match to the data you are
displaying, and computing a fixed width for the bars isn't too
onerous.

Mike

Rob Dodson

unread,
May 2, 2012, 8:40:43 PM5/2/12
to d3...@googlegroups.com
Out of curiosity I wanted to try the ordinal approach. Not sure why but my x-axis ticks got totally garbled. I was expecting to see full timestamps but not sure where those integers came from...?

http://jsfiddle.net/robdodson/PcHQh/1/

Nate Agrin

unread,
May 3, 2012, 1:38:17 AM5/3/12
to d3...@googlegroups.com
Rob, I ended up coming up with a solution that works for us. I'll post
the code tomorrow.

Mike, thanks for following up and confirming this methodology.

-Nate

Rob Dodson

unread,
May 3, 2012, 1:42:44 AM5/3/12
to d3...@googlegroups.com
oh great thanks Nate!

Nate Agrin

unread,
May 6, 2012, 2:26:41 AM5/6/12
to d3...@googlegroups.com
Rob, sorry about the delay. Below I've outlined our general technique.
It's not a finalized solution but it should help you get on your way.

// Let's say you have a time scale
var x = d3.time.scale();

// Now you set the domain somewhere...
x.domain([new Date(2000, 0, 1), new Date(2001, 0, 1)]);

// and set the range somewhere...
x.range([0, 500]);

// Now later on in your code you detect that you're trying to render
// something like a bar chart that represents some kind of 'buckets' of
// data (ie a histogram). In my example below you need to know the size of
// your buckets. You could write some algorithm which determines the bucket
// size. In the following example we will assume we know the bucket
size is days.

// Determine the number of buckets. Using the d3.time.* methods you may need to
// add one additional unit of time to the second parameter, as I do below.
// Your milage may vary; play around with adding the extra day and see how it
// renders differently.

var domain = x.domain();
var maxDay = new Date(domain[1]);
maxDay.setDate(maxDay.getDate() + 1);
var buckets = d3.time.days(domain[0], maxDay);
var width = d3.scale.ordinal().domain(buckets).rangeRoundBands(x.range(),
0.2).rangeBand();

// `width` can now be used to render the bars in your bar chart as you
would with an ordinal scale.

// This technique could be improved by shifting the d3.svg.axis labels
(width / 2) pixels. Experiment and you should see what I mean.

Sjors

unread,
Sep 6, 2012, 10:09:53 AM9/6/12
to d3...@googlegroups.com
or something simple like this:

x = d3.time.scale().range([0,1]);

.attr("x", function (d) { return (x(d.timestamp)*(w-(w/data.length)*0.8));})
.attr("width", (w/data.length)*0.8);

(w = width of your canvas)

Op zondag 6 mei 2012 08:26:41 UTC+2 schreef n8agrin het volgende:

Fil Mackay

unread,
Mar 20, 2013, 1:09:41 AM3/20/13
to d3...@googlegroups.com
Isn't there a use-case that is not covered by D3 in any way? That is: sparse date-time data? Think of stock prices, where you want to exclude weekends, holidays etc. on the x-axis?

As far as I can see all the D3 demos with stock data conveniently use line charts to hide the fact that there is gaps in the data. Mike if you wanted to use a bar chart - how would you implement it, yet have a cleanly labelled X axis?

You kind of want an ordinal axis (to compress the sparseness) but have intelligent labeling, rather than label every observation :)

So, has anyone implemented a d3.time.scale.ordinal() ?

Alex Brown

unread,
Mar 20, 2013, 3:08:14 AM3/20/13
to d3...@googlegroups.com
cf my contemporaty replies about linear range bands - just another request.

Sachin Sharma

unread,
Nov 6, 2015, 12:59:30 PM11/6/15
to d3-js
I have exactly the same concern as you. This is a pretty old thread. If you have found some solution, please help me out as well. 
Reply all
Reply to author
Forward
0 new messages