About this book

d3.js. provides a platform that help you create your own beautiful visualization and bring data to life using HTML, SVG and CSS. It emphasis on web standards that will fully utilize the capabilities of your web browser.

Data Visualization with d3.js walks you through 20 examples in great detail. You can finally stop struggling to piece together examples you've found online. With this book in hand, you will learn enough of the core concepts to conceive of and build your own visualizations from scratch.

The book begins with the basics of putting lines on the screen, and builds on this foundation all the way to creating interactive animated visualizations using d3.js layouts.

You will learn how to use d3.js to manipulate vector graphics with SVG, layout with HTML, and styling with CSS. You'll take a look at the basics of functional programming and using data structures effectively – everything from handling time to doing geographic projections. The book will also help make your visualizations interactive and teach you how automated layouts really work.

Data Visualization with d3.js will unveil the mystery behind all those beautiful examples you've been admiring.

Publication date:
October 2013
Publisher
Packt
Pages
194
ISBN
9781782160007

 

Chapter 1. Getting Started with d3.js

In this chapter, I'll show you the basic tools for making simple visualizations in d3.js without going into too much depth so that you can get started immediately. We will go through the basic language of d3.js and also its rules.

We'll take a stab at creating axes and automatically scaling graphs to fit the viewport, and learn about using Chrome Developer Tools to model our code before going into a full-blown programming bonanza. Through this chapter, we're going to set up the environment used throughout the book and create an animated chart of a dataset I created from GitHub.

 

What is d3.js?


The name D3 stands for Data-Driven Documents. Mike Bostock has been openly developing this powerful data visualization library since 2011. It helps you draw beautiful graphics by manipulating data without worrying too much about pixel positions, calculating where things fit on a graph, and so on. If you've ever visualized data in Python or similar languages, you've probably used something similar to gnuplot . I assure you that d3.js offers a much more pleasurable experience.

The official website, d3js.org, features many great examples that show off the power of d3.js, but understanding them is tricky at best. After finishing this book, you should be able to understand d3.js well enough to figure out the examples. If you want to follow the development of d3.js more closely, the source code is hosted on GitHub at https://github.com/mbostock/d3.

The fine-grained control and its elegance make d3.js one of the most, if not the most, powerful open source visualization libraries out there. This also means that it's not very suitable for simple jobs such as drawing a chart or two—in that case you might want to use a library designed for charting. Many use d3.js internally anyway.

As a data manipulation library, d3.js is based on functional programming principles, which is probably where a lot of the confusion stems from. Unfortunately, functional programming goes beyond the scope of this book, but I'll explain all the relevant bits to make sure everyone's on the same page.

 

Setting up a play environment


D3 combines HTML, CSS, and SVG to create graphics. That means we're going to need an HTML and a JavaScript file. We'll use Chrome Developer Tools to tweak our visualizations and test things out. Let's start with some HTML coding:

<!DOCTYPE html>
<title></title>
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">

<div id="graph"></div>

<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="code.js"></script>

These six lines of HTML code are the basics we're going to use throughout this book.

The first two lines comprise a minimal HTML5 document. You no longer need to include the <html>, <head>, and <body> tags. Next is the <link> tag that pulls in Twitter Bootstrap's CSS rules—a good set of defaults to make things prettier. After that comes the <div> tag that will hold our visualization, and finally, there's the <script> tag that loads d3.js.

At the end, we include a code.js file, where we'll put most of our code. Twitter doesn't offer a hosted version of Bootstrap, so you have to download the whole package from http://twitter.github.com/bootstrap/ and unpack it next to the other files you're working with. All we need now is a server to run everything. This is because we don't want to get into trouble with browser security models when making Ajax requests. Any server will do, but here's a quick way to get one up and running if you already have Python installed (by default on Mac and Linux).

Fire up a console, navigate to your working directory, and run the following command:

$ python -m SimpleHTTPServer

Python will run the SimpleHTTPServer module as a standalone script and create a fully functional local server.

Now point Chrome to localhost:8000 and fire up the developer console—Ctrl + Shift + J for Linux and Windows and Option + Command + J for Mac. You should see a blank website and a blank JavaScript console with a command prompt waiting for some code:

A quick Chrome Developer Tools primer

Chrome Developer Tools are indispensable in web development. Most modern browsers have something similar, but I thought we'd stick to a single example to keep the book shorter. Feel free to use a different browser.

We are mostly going to use the Elements and Console tabs: Elements to inspect the DOM, and Console to play with JavaScript code and look for any problems.

The other six tabs come in handy for large projects. The Network tab will let you know how long files are taking to load and helps you inspect the Ajax requests. The Profiles tab will help you profile JavaScript for performance. The Resources tab is good for inspecting client-side data. Honestly, I have never needed Timeline and Audits before. One of the favorites from Developer Tools is the CSS inspector at the right-hand side of the Elements tab.

It can tell you what CSS rules are affecting the styling of an element, which is very good for hunting rogue rules that are messing things up. You can also edit the CSS and immediately see the results:

 

A simple histogram


We'll go through the basics of d3.js by creating a histogram indicating when the GitHub users commit code. We're going to label axes, make sure things are scalable, and modify animations for that extra bit of flair.

The dataset contains 504,015 repositories and it took me a week to create it out of punchcard data for each repository. A punchcard is just a 7 x 24 grid of buckets, specifying how many commits happened within a specific day and hour. The dataset's histogram digest is hosted at http://nightowls.swizec.com/data/histogram-hours.json and maps hours to the sum of commits occurring within that hour.

This is what we're aiming for:

We begin by taking the environment prepared in the previous section and adding a few lines around the central <div> tag:

<div class="container">
  <div class="row">
    <div id="graph" class="span12"></div>
  </div>
</div>

The extra <div> tags center the graph horizontally and ensure that we have 900 px of width to work with. Don't forget to add the class="span12" parameter into the graph div. It tells Bootstrap the div should go the whole width of the grid.

To avoid tripping your browser's security restrictions regarding cross-domain requests, you should now take a moment to download the dataset and save it next to the other files. Remember, it's at http://nightowls.swizec.com/data/histogram-hours.json.

You can play around with the following code in Chrome Developer Tools to see what it does and then save it in code.js. Writing directly to the file also works, but just make sure you refresh frequently. Learning is if you know what each line does.

We begin with some variables as follows:

var width = 900, height = 300, pad = 20, left_pad = 100;

We're going to use these to specify the dimensions of our drawing area. The pad variable will define the padding from the edge, with left_pad giving a bigger margin on the left to allow for labels.

Next, we define a horizontal scale, x:

var x = d3.scale.ordinal().rangeRoundBands([left_pad, width - pad], 0.1);

The x scale is now a function that maps inputs from a yet unknown domain (we don't have the data yet) to a range of values between left_pad and width - pad, that is, between 100 and 880 with some spacing defined by the 0.1 value. Because it's an ordinal scale, the domain will have to be discrete rather than continuous. rangeRoundBands means the range will be split into bands that are guaranteed to be round numbers.

Then, we define another scale named y:

var y = d3.scale.linear().range([height-pad, pad]);

Similarly, the y scale is going to map a yet unknown linear domain to a range between height-pad and pad, that is, 880 and 20. Inverting the range is important because d3.js considers the top of a graph to be y=0.

Now, we define our axes as follows:

var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");

We've told each axis what scale to use when placing ticks and which side of the axis to put the labels on. D3 will automatically decide how many ticks to display, where they go, and how to label them.

The last step before loading the data is defining an SVG element for the histogram:

var svg = d3.select("#graph").append("svg")
                .attr("width", width).attr("height", height);

Switching quickly to the Elements tab, you'll notice a new HTML element with a width of 900 and a height of 100.

Now the fun begins!

We're going to use d3.js itself to load data remotely and then draw the graph in the callback function. Remember to use Shift + Enter to input multiline code in the Chrome console. Now might be a good time to switch to coding in code.js directly and refreshing after every couple of steps:

d3.json('histogram-hours.json', function (data) {
});

d3.json will create an Ajax request to load a JSON file, then parse the received text into a JavaScript object. D3 understands CSV and some other data formats as well, which is kind of awesome if you ask me.

From here on, we put everything in that callback function (before the }); bit). Our data will be in the data variable. D3 is a functional data-munging library, so we need to transform our dictionary data into a list of simple objects. We do this using the following code:

data = d3.keys(data).map(function (key) {
  return {bucket: Number(key),
    N: data[key]};
  });

d3.keys returns a list of keys in the data dictionary, which we then map over with an iterator function that returns a simple dictionary for every item. It tells us where an item fits in the histogram (bucket) and what value it holds (N).

We've turned our data into a list of two-value dictionaries.

Remember the x and y scales from before? We can finally give them a domain and make them useful:

    x.domain(data.map(function (d) { return d.bucket; }));
    y.domain([0, d3.max(data, function (d) { return d.N; })]);

Since most d3.js elements are objects and functions at the same time, we can change the internal state of both scales without assigning the result to anything. The domain of x is a list of discrete values. The domain of y is a range from 0 to d3.max of our dataset—the largest value.

Now we're going to draw the axes on our graph:

svg.append("g")
  .attr("class", "axis")
  .attr("transform", "translate(0, "+(height-pad)+")")
  .call(xAxis);

We've appended an element called g to the graph, given it the CSS class "axis", and moved the element to a place at the bottom-left of the graph with the transform attribute.

Finally, we call the xAxis function and let d3.js handle the rest.

Drawing the other axis works exactly the same, but with different arguments:

svg.append("g")
  .attr("class", "axis")
  .attr("transform", "translate("+(left_pad-pad)+", 0)")
  .call(yAxis);

Now that our graph is labeled, it's finally time to draw some data:

svg.selectAll('rect')
  .data(data)
  .enter()
  .append('rect')
  .attr('class', 'bar')
  .attr('x', function (d) { return x(d.bucket); })
  .attr('width', x.rangeBand())
  .attr('y', function (d) { return y(d.N); })
  .attr('height', function (d) { return height-pad - y(d.N); });

Okay, there's plenty going on here, but this code is saying something very simple: for all rectangles (rect) in the graph, load our data, go through it, and for each item append a rect and then define some attributes.

The x scale helps us calculate the horizontal positions and rangeBand gives the width of the bar. The y scale calculates vertical positions and we manually get the height of each bar from y to the bottom. Note that whenever we needed a different value for every element, we defined an attribute as a function (x, y, and height); otherwise, we defined it as a value (width).

Keep this in mind when you're tinkering.

Let's add some flourish and make each bar grow out of the horizontal axis. Time to dip our toes into animations!

Add five lines to the preceding code:

svg.selectAll('rect')
  .data(data)
  .enter()
  .append('rect')
  .attr('class', 'bar')
  .attr('x', function (d) { return x(d.bucket); })
  .attr('width', x.rangeBand())
  .attr('y', height-pad)
  .transition()
  .delay(function (d) { return d.bucket*20; })
  .duration(800)
  .attr('y', function (d) { return y(d.N); })
  .attr('height', function (d) { return height-pad - y(d.N); });

The difference is that we statically put all bars at the bottom (height-pad) and then entered a transition with .transition(). From here on, we define the transition we want.

First, we wanted each bar's transition delayed by 20 milliseconds using d.bucket*20. This gives the histogram a neat effect, gradually appearing from left to right instead of jumping up at once. Next, we said we wanted each animation to last just shy of a second with .duration(800). In the end, we defined the final values for the animated attributes—y and height are the same as in previous code—and d3.js is going to take care of the rest.

Refresh the page and voila! A beautiful histogram appears as shown in the following screenshot:

Hmm, not really. We need some CSS to make everything look perfect.

Remember that you can look at the full code on GitHub at https://github.com/Swizec/d3.js-book-examples/tree/master/ch1 if you didn't get something similar to the preceding screenshot.

Let's go into our HTML file and add some CSS on line 4, right after including bootstrap:

<style>
  .axis path,
  .axis line {
    fill: none;
    stroke: #eee;
    shape-rendering: crispEdges;
  }

  .axis text {
    font-size: 11px;
  }

  .bar {
    fill: steelblue;
  }
</style>

This is why we added all those classes to shapes. We made the axes thin, gave them a light-gray color, and used a relatively small font for the labels. The bars should be steel blue. Refresh the page now and the histogram is beautiful:

I suggest playing around with the values for width, height, left_pad, and pad to get a feel of the power of d3.js. You'll notice everything scales and adjusts to any size without having to change the other code. Marvelous!

 

Summary


We've learned what d3.js is and took a glance at the core philosophy behind how it works. We've also set up a quick and easy environment for prototyping ideas and playing with visualizations. This environment will be assumed throughout the book.

We've also gone through a simple example and created an animated histogram using some of the basics of d3.js. We found out about scales and axes, that the vertical axis is inverted, that any property defined as a function is recalculated for every data point, and that we use a combination of CSS and SVG to make things beautiful.

Most of all, this chapter has given you the basic tools so that you can start playing with d3.js on your own. Tinkering is your friend.

About the Author

  • Swizec Teller

    Swizec Teller, author of Data Visualization with d3.js, is a geek with a hat. He founded his first start-up at the age of 21 years and is now looking for the next big idea as a full-stack Web generalist focusing on freelancing for early-stage start-up companies. When he isn't coding, he's usually blogging, writing books, or giving talks at various non-conference events in Slovenia and nearby countries. He is still looking for a chance to speak at a big international conference. In November 2012, he started writing Why Programmers Work At Night, and set out on a quest to improve the lives of developers everywhere.

    Browse publications by this author
Book Title
Unlock this full book FREE 10 day trial
Start Free Trial