Raspberry Pi Pico Tips and Tricks

Friday, 2 August 2013

Add a line chart in dc.js

The following post is a portion of the D3 Tips and Tricks book which is free to download. To use this post in context, consider it with the others in the blog or just download the the book as a pdf / epub or mobi .
----------------------------------------------------------
The line chart is another simple choice for implementation using crossfilter and dc.js.
The line chart that we'll create will be a representation of the frequency of the occurrence of the earthquakes that we have in our dataset. In this respect, what we are expecting to see is the number of events on the y axis and the time-scale on the x axis.

It should end up looking a bit like this.
Just as with the bar chart, we'll work through adding the chart in the following stages.
  1. Position the chart
  2. Assign type
  3. Dimension and Group
  4. Configure chart parameters

Position the line chart

We are going to position our line chart above our data table (and below the bar charts)and we'll make it the full width of our data table so that it looks like it belongs there.
Just under the line of code that defined the containers for the bar graphs;
  <div class='row'>
    <div class='span6' id='dc-magnitude-chart'>
      <h4>Events by Magnitude Counted</h4>
    </div>
    <div class='span6' id='dc-depth-chart'>
      <h4>Events by Depth (km)</h4>
    </div>   
  </div>
We add in a new row that has a single span12.
  <div class='row'>
    <div class='span12' id='dc-time-chart'>
      <h4>Events per hour</h4>
    </div>
  </div>
We've given it an ID selector of dc-time-chart. So when we we assign our chart that selector, it will automatically appear in that position. We've also put another simple title in place (<h4>Events per hour</h4>).

Assign the line chart type

Here we give our chart it's name (timeChart), assign it with a dc.js chart type (in this case lineChart) and assign it to the ID selector (dc-time-chart).

Under the line that assigns the depthChart chart type...
  var depthChart = dc.barChart("#dc-depth-chart");
... add in the equivalent for our line chart.
  var depthChart = dc.barChart("#dc-depth-chart");
  var timeChart = dc.lineChart("#dc-time-chart");
Nice.

Dimension and group the line chart data

We'll put the code between the dimension and group of the depth chart and the data table dimension (this is just to try and keep the code in the same order as the graphs on the page).

To set our dimension for our time we do something a little different.
  var volumeByHour = facts.dimension(function(d) {
    return d3.time.hour(d.dtg);
  });
This dimension (volumeByHour) uses the same facts data, but when the key values are returned (return d3.time.hour(d.dtg);) we are going to return the information by hours. This is essentially defining the resolution of the values on the x axis for our line chart.

Then we want to group the data by counting the number of events of for each hour.
  var volumeByHourGroup = volumeByHour.group()
    .reduceCount(function(d) { return d.dtg; });
This piece of code (which should do directly under the volumeByHour dimension portion), groups (.group()) by counting (.reduceCount) all of the magnitude values (function(d) { return d.dtg; })) and assigns it to the volumeByHourGroup variable. This has defined the values for the y axis of our line chart (the number of events we see in a given hour).

Configure the line chart parameters

As with the bar chart, there are lots of parameters that can be configured. The best way to learn what they do is by having a play with them. So here is the block of code for configuring the line chart. Once you are happy that it works on your system, take some time and go through the settings in conjunction with the information from the demo page and the api reference.

This should go just before the block that configures the dataTable (again, this is just to try and keep the code in the same order as the graphs on the page).
  // time graph
  timeChart.width(960)
    .height(150)
    .margins({top: 10, right: 10, bottom: 20, left: 40})
    .dimension(volumeByHour)
    .group(volumeByHourGroup)
    .transitionDuration(500)
    .elasticY(true)
    .x(d3.time.scale().domain([new Date(2013, 6, 18), new Date(2013, 6, 24)]))
    .xAxis();
That should be it. With the addition of this portion of the code, you should have a functioning visualization that can be filtered dynamically. Just check to make sure that everything is working properly and we'll go through some of the configuration options to see what they do.

To start with, your page should look something like this;
The configuration options start by declaring the name of the chart (timeChart) and setting the height and width of the chart.
  timeChart.width(960)
    .height(150)
In the case of our example I have selected the width based on the default size for a span12 grid segment in bootstrap and adjusted the height to make it look suitable.

Then we have our margins set up.
    .margins({top: 10, right: 10, bottom: 20, left: 40}) 

Nothing too surprising there although the left margin is slightly larger to allow for larger values on the y axis to be represented without them getting clipped (not strictly for this example, but it's a handy default).

Then we define which dimension and grouping we will use.
    .dimension(volumeByHour)
    .group(volumeByHourGroup)
Think of the .dimension declaration being the x axis and the .group declaration being the y axis.

The .transitionDuration setting defines the length of time that any change takes to be applied to the chart as it adjusts.
    .transitionDuration(500)
We can set the y axis to dynamically adjust when the number of events are filtered by selections on any of the other charts.
    .elasticY(true)
For instance if we select only earthquakes with a magnitude between 4 and 5, our line chart will have a maximum value on the y axis of 7 events;
However, if we select all the earthquakes, the y axis will dynamically adjust to over 30.
Since the line chart has an x axis which is made of date/time values, we set our scale and domain using thed3.time.scale declaration.
    .x(d3.time.scale().domain([new Date(2013, 6, 18), new Date(2013, 6, 24)]))
This is hard coded for our date range, but a smarter method would be to have the scale adjust to suit your range of date/time values automatically with the following line;
    .x(d3.time.scale().domain(d3.extent(data, function(d) { return d.dtg; })))
Using the d3.extent function means that our line graph of time now spans the exact range of our data values on the x axis (note that the time scale now starts just before the 18th and ends when our data ends).
The final parameter that we set is to add the x axis.

    .xAxis();

Adding tooltips to a line chart

dc.js has a nice feature for adding tooltips to a line chart.
It utilises the .title function in the configuration of the chart to apply the tooltip, but the downside is that the ability to select the time range needs to be disabled (there are ways to compensate for this which I hope to cover in the future).
If we take our example line chart configuration block of code;
  // time graph
  timeChart.width(960)
    .height(150)
    .margins({top: 10, right: 10, bottom: 20, left: 40})
    .dimension(volumeByHour)
    .group(volumeByHourGroup)
    .transitionDuration(500)
    .elasticY(true)
    .x(d3.time.scale().domain([new Date(2013, 6, 18), new Date(2013, 6, 24)]))
    .xAxis();
We need to turn off the .brushOn feature (.brushOn(false)) that allows for selection and add in the.title` function as follows;
  // time graph
  timeChart.width(960)
    .height(150)
    .margins({top: 10, right: 10, bottom: 20, left: 40})
    .dimension(volumeByHour)
    .group(volumeByHourGroup)
    .transitionDuration(500)
    .brushOn(false)
    .title(function(d){
      return d.data.key
      + "\nNumber of Events: " + d.data.value;
      })
    .elasticY(true)
    .x(d3.time.scale().domain([new Date(2013, 6, 18), new Date(2013, 6, 24)]))
    .xAxis();

As we can see, the tooltip is using the default time format for the script from our keyvalue (on the x axis), and as a result, the representation of the date / time is quite long winded. We can adapt this to a format of our choosing by calling a time formatting function similar to the following;
  var dtgFormat2 = d3.time.format("%a %e %b %H:%M");
This line could ideally go after the other time formatting function (dtgFormat) that occurs earlier in the script. The formatting it's introducing can be found in the d3.js wiki, but in short it returns the date / time formatted as abbreviated weekday name, day of the month as a decimal number, abbreviated month name and 24 hour clock hour:minute.
With our function in place, the .title. call from our line chart configuration code would now look like this;
    .title(function(d){
      return dtgFormat2(d.data.key)
      + "\nNumber of Events: " + d.data.value;
      })
And the resulting graph looks like this;
We also add in the number of the events from the y axis (d.data.value), separated with a new line character (\n) and some appropriate text.

The description above (and heaps of other stuff) is in the D3 Tips and Tricks book that can be downloaded for free (or donate if you really want to :-)).

No comments:

Post a Comment