Tuesday, 5 February 2013

More d3.js table madness: sorting, prettifying and adding columns


The following post is a portion of the D3 Tips and Tricks document which is free to download. To use this post in context, consider it with the others in the blog or just download the pdf  and / or the examples from the downloads page:-)
-------------------------------------------------------

When we last left our tables they were happily producing a faithful list of the data points that we had in our graph.

But what if we wanted more?

From the original contributors that bought you tables (Shawn Allen (http://jsfiddle.net/7WQjr/) on Google Groups (http://stackoverflow.com/questions/9268645/d3-creating-a-table-linked-to-a-csv-file)) and some neat additions from Christophe Viau (http://christopheviau.com/d3_tutorial/) comes extra coolness that I didn't include in the previous example :-).

Add another column of information:

Firstly, lets add another column of data to our table. To do this we want to have something extra in our tsv file to use, so let's resurrect our old friend data2.tsv that we have used for the graph with two lines previously. All we have to do to make this a reality is change the reference that loads `data.tsv` to `data2.tsv` here;
d3.tsv("data/data2.tsv", function(error, data) {
(this makes the assumption that you still have the data2.tsv file in place. If not, rush away and get it from d3noob.org's downloads page)

From here (and as promised in the previous chapter), it's just a matter of adding in the extra column you want (in this case it's the `open` column) like so;
var peopleTable = tabulate(data, ["date", "close", "open"]);

(and yes, if you're wondering, I have cheated slightly and changed the table indent to make it look slightly prettier)

So can we go further?

You know we can...

In the section where we get our data and format it, lets add another column to our array in the form of a difference between the `close` value and the `open` value (and we'll call it `diff`).
d3.tsv("data/data2.tsv", function(error, data) { 
        data.forEach(function(d) {
                d.date1 = parseDate(d.date);
                d.close = +d.close;
                d.open = +d.open;                                 //  <= added this for tidy house keeping
                d.diff = Math.round(( d.close - d.open ) * 100 ) / 100;  // <= The new diff column
        });
(the `Math.round` function is to make sure we get a reasonable figure to display, otherwise it tends to get carried away with decimal places)

So now we add in our new column to be tabulated;
var peopleTable = tabulate(data, ["date", "close", "open", "diff"]);
… and viola!
(And yes, I changed the table indent again. I am a serial offender and will continue to change it to suit)

Sorting on a column

So now with our four columns of awesome data, it turns out that we're really interested in the ones that have the highest `close` values. So we can sort on the `close` column and display by adding the following lines directly after the line where we declare the `peopleTable` function (which I will include in the code snipped below for reference).
var peopleTable = tabulate(data, ["date", "close", "open", "diff"]); 

peopleTable.selectAll("tbody tr") 
        .sort(function(a, b) {
                return d3.descending(a.close, b.close);
        });
Which works magnificently;




Prettifying (actually just capitalising the header for each column)

Just a little snippet that capitalises the headers for each row to make them look slightly more authoritative.

Add the following lines of code directly below the block that you just added for sorting the table;
 peopleTable.selectAll("thead th")
        .text(function(column) {
                return column.charAt(0).toUpperCase() + column.substr(1);
        });
This is quite a tidy little piece of script. You can see it selecting the headers (`selectAll("thead th")`), then the first character in each header (`column.charAt(0)`), changing it to upper-case (`.toUpperCase()`) and adding it back to the rest of the string (`+ column.substr(1)`).

With the ultimate result...


Add borders

Sure our table looks nice and neatly arranged, but would a border look better?

Well, here's one way to do it;

All we need to do is add a border style to our table by adding in this line here;
function tabulate(data, columns) {
    var table = d3.select("body").append("table")
            .attr("style", "margin-left: 200px")   // <= Remove the comma
            .style("border", "2px black solid"),   // <= Add this line in
        thead = table.append("thead"),
        tbody = table.append("tbody");
(don't forget to move the comma from the end of the `margin-left` line)

And the result is a tidy black border.


OK, so what about the individual cells?

No problem.

If we remember back to our CSS that we added in, we'll just tell each cell that we want a 1 pixel border buy amending the CSS for our table to this;
td, th {
    padding: 1px 4px;
    border: 1px black solid;
}
So now each cell has a slightly more subtle border like this;

Yikes! Not quite as subtle as I would have expected. I suppose it's another example of the code actually doing what you asked it to do. No problem, `border-collapse` to the rescue. Add the following line into here;
function tabulate(data, columns) {
    var table = d3.select("body").append("table")
            .attr("style", "margin-left: 200px")
            .style("border-collapse", "collapse")         // <= Add this line in.
            .style("border", "2px black solid"),
        thead = table.append("thead"),
        tbody = table.append("tbody");

How does that look?

Ahh.... Much more refined.

The `border-collapse` style tells the table to overlap each cells borders, rather than treat them as discrete entities. So in this case it looks a bit better.


This file has been saved as table-plus-addins.html and has been added into the downloads section on d3noob.org with the general examples files.

The above description (and heaps of other stuff) is in the D3 Tips and Tricks document that can be accessed from the downloads page of d3noob.org.

7 comments:

  1. Why always show images of the rendered d3 output and not show the real d3 html/svg? At least you could link to a jsFiddle of your final version.

    ReplyDelete
  2. Good question.
    There are three contributing reasons.
    1. The images that you see here are actually the images that I create to put into the book (https://leanpub.com/D3-Tips-and-Tricks). So it's a one-stop-effort for me to grab the image put it into the book and then to (later) put them on the blog. So this IS the least I could do :-).
    2. If I included the svg there is a good chance that some IE users would have trouble viewing them (I know the proportion is small, but even IE users need a break sometimes).
    3. I've got very little time. If this was anything but a labour of love I would try and improve it any way I can, but I'm just too stretched. I would be more than happy to link to fiddles generated by others if they wanted to contribute :-)

    ReplyDelete
  3. Great blog by the way!
    Was just wondering if it was possible to update the values in the table, similarly to what you have done in the previous section to update a graph?
    Any help appreciated! :)
    Noob

    ReplyDelete
    Replies
    1. That would be possible, but the method used in the previous post is a bit crude. I really need to work on a more elegant mechanism for doing it that is more in keeping with the ethos of d3.js. I envisage either a regular check for updated values via a delay and then re-display (but nont in the same way as the previous post) or (more interestingly, a connection from an external socket. This would be the most interesting and extensible variation. Unfortunately it will take a while for me to learn how to do this. My best advice if want to pursue the table update is to have a play with a very simple example perhaps a single line of text) and then graduate to the table example. Good luck.

      Delete
  4. Where is the example file "table-plus-addins.html"? Cannot find it anywhere in your downloads page? Thanks!

    ReplyDelete
    Replies
    1. Really good question! Since I posted the blog in 2013, I have moved the location of the code samples to be co-located with the book on Leanpub. You can download the book for free from here https://leanpub.com/D3-Tips-and-Tricks and once you have that you can download the code samples (still for free) This particular sample was renamed simple-graph-plus-table.html and you can also get a copy from the live example here http://bl.ocks.org/d3noob/473f0cf66196a008cf99 Interestingly, this is a mistake in the book that has existed for at least three years (and probably more) which no-one has picked up. I will edit the book now and re-publish it so that those who download it from now on will have the right info. Cheers!

      Delete
    2. Sorry, you were asking about the other table example! Just realised. That is also in the code sample extras and is called simple-graph-plus-table-plus-addins.html the live example is here; http://bl.ocks.org/d3noob/5d47df5374d210b6f651

      Delete