D3 Tips and Tricks v4

Saturday, 3 September 2016

Formatting a date / time axis with specified value in v4

The following post is a section of the book 'D3 Tips and Tricks v4.x'.  The entire book can be downloaded in pdf format for free from Leanpub or you can read it online here.
Since this post is a snapshot in time. I recommend that you download a copy of the book which is updated frequently to improve and expand the content.
---------------------------------------
As referenced in the post where we initially developed our simple graph, the axes of that graph had no styling or configuration changes made to them at all. One of the results of this is that the font size, type, number of ticks and the way that the values are represented is very much at the default settings.

Formatting a date / time axis with specified values

OK then. We’ve been very clever in rotating our text, but you will notice that D3 has used its own good judgement as to what format the days / date will be represented as.
Not that there’s anything wrong with it, but what if we want to put a specific format of date / time nomenclature as axis labels?
No problem. D3 to the rescue again!
This is actually a pretty easy thing to do, but there are plenty of options for the formatting, so the only really tricky part is deciding what to put where.
But, before we start doing anything we are going to have to expand our bottom margin even more than we did with the rotate the axis labels feature.
var margin = {top: 20, right: 20, bottom: 100, left: 50},
That should see us right.
Now the simple part :-). Changing the format of the label is as simple as inserting the tickFormat command into the xAxis declaration and including a D3 time formatting function a little like this;
  svg.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x)
              .tickFormat(d3.timeFormat("%Y-%m-%d")))
      .selectAll("text") 
        .style("text-anchor", "end")
        .attr("dx", "-.8em")
        .attr("dy", ".15em")
        .attr("transform", "rotate(-65)");
The timeFormat formatters are the same as those we used when parsing our time values when reading our data with the simple graph;
  • %a - abbreviated weekday name.
  • %A - full weekday name.
  • %b - abbreviated month name.
  • %B - full month name.
  • %c - date and time, as “%a %b %e %H:%M:%S %Y”.
  • %d - zero-padded day of the month as a decimal number [01,31].
  • %e - space-padded day of the month as a decimal number [ 1,31].
  • %H - hour (24-hour clock) as a decimal number [00,23].
  • %I - hour (12-hour clock) as a decimal number [01,12].
  • %j - day of the year as a decimal number [001,366].
  • %m - month as a decimal number [01,12].
  • %M - minute as a decimal number [00,59].
  • %p - either AM or PM.
  • %S - second as a decimal number [00,61].
  • %U - week number of the year (Sunday as the first day of the week) as a decimal number [00,53].
  • %w - weekday as a decimal number [0(Sunday),6].
  • %W - week number of the year (Monday as the first day of the week) as a decimal number [00,53].
  • %x - date, as “%m/%d/%y”.
  • %X - time, as “%H:%M:%S”.
  • %y - year without century as a decimal number [00,99].
  • %Y - year with century as a decimal number.
  • %Z - time zone offset, such as “-0700”.
  • There is also a literal “%” character that can be presented by using double % signs.
So the format we have specified (%Y-%m-%d) will show the year with the century as a decimal number (%Y) followed by a hyphen, followed by a zero padded month as a decimal number (%m) followed by another hyphen and lastly the day as a zero padded day of the month (%d).
The end result looking a bit like this;
Rotated x axis labels
An example using this code can be found on github or in the code samples bundled with this book (simple-axis-rotated-formatted.html and data.csv). A working example can be found on bl.ocks.org. The example code also includes the rotating of the x axis text as described in the previous section.
So how about we try something a little out of the ordinary (extreme)?
How about the full weekday name (%A), the day (%d), the full month name (%B) and the year (%Y) as a four digit number?
  svg.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x)
              .tickFormat(d3.timeFormat("%A %d %B %Y")))
      .selectAll("text") 
        .style("text-anchor", "end")
        .attr("dx", "-.8em")
        .attr("dy", ".15em")
        .attr("transform", "rotate(-65)");
We will also need some extra space for the bottom margin, so how about 170?
var margin = {top: 20, right: 20, bottom: 170, left: 50},
And….
Extreme format change for the x axis labels
Oh yeah… When axis ticks go bad…
But seriously, that does work as a pretty good example of the flexibility available.

The post above (and heaps of other stuff) is in the book 'D3 Tips and Tricks v4.x' that can be downloaded for free (or donate to encourage further development :-)).

Wednesday, 31 August 2016

Rotating text labels for a graph axis in v4

The following post is a section of the book 'D3 Tips and Tricks v4.x'.  The entire book can be downloaded in pdf format for free from Leanpub or you can read it online here.
Since this post is a snapshot in time. I recommend that you download a copy of the book which is updated frequently to improve and expand the content.
---------------------------------------

As referenced in the post where we initially developed our simple graph, the axes of that graph had no styling or configuration changes made to them at all. One of the results of this is that the font size, type, number of ticks and the way that the values are represented is very much at the default settings.

Rotating text labels for a graph axis

An answer to the problem of overlapping axis values might be to rotate the text to provide more space.
The answer I found most usable was provided by Aaron Ward on Google Groups.
The full code for this example can be found on github or in the code samples bundled with this book (simple-axis-rotated.html and data.csv). A working example can be found on bl.ocks.org.
The first substantive change would be a little housekeeping. Because we are going to be rotating the text at the bottom of the graph, we are going to need some extra space to fit in our labels. So we should change our bottom margin appropriately.
var margin = {top: 20, right: 20, bottom: 70, left: 50},
I found that 70 pixels was sufficient.
The remainder of our changes occur in the block that draws the x axis.
  // Add the X Axis
  svg.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x).ticks(10))
      .selectAll("text") 
        .style("text-anchor", "end")
        .attr("dx", "-.8em")
        .attr("dy", ".15em")
        .attr("transform", "rotate(-65)"); 
It’s pretty standard until the .call(d3.axisBottom(x).ticks(10)) portion of the code. Here we remove the semicolon that was there so that the block continues with its function.
Then we select all the text elements that comprise the x axis with the .selectAll("text"). From this point onward, we are operating on the text elements associated with the x axis. In effect; the following four ‘actions’ are applied to the text labels.
The .style("text-anchor", "end") line ensures that the text label has the end of the label ‘attached’ to the axis tick. This has the effect of making sure that the text rotates about the end of the date. This makes sure that the text all ends up at a uniform distance from the axis ticks.
The dx and dy attribute lines move the end of the text just far enough away from the axis tick so that they don’t crowd it and not too far away so that it appears disassociated. This took a little bit of fiddling to ‘look’ right and you will notice that I’ve used the ‘em’ units to get an adjustment if the size of the font differs.
The final action is kind of the money shot.
The transform attribute applies itself to each text label and rotates each line by -65 degrees. I selected -65 degrees just because it looked OK. There was no deeper reason.
The end result then looks like the following;
Rotated x axis labels

This was a surprisingly difficult problem to find a solution to that I could easily understand (well done Aaron). That makes me think that there are some far deeper mysteries to it that I don’t fully appreciate that could trip this solution up. But in lieu of that, enjoy!

The post above (and heaps of other stuff) is in the book 'D3 Tips and Tricks v4.x' that can be downloaded for free (or donate to encourage further development :-)).