D3.js Tutorial using HTML, Scales and Chili Peppers

By

If you want to create visualisations for the web, the tool you will want to learn is D3.js. It helps you build powerful visualisations that can express your data while remaining light-weight and flexible. However, D3 is one of those tools that has its own way of doing things that is a little bit different from other visualisation libraries. It has a philosophy that focuses on data and functions, and not visual elements. This may make it difficult to learn because to wrap your head around it you need an epiphany, where you just get how to use it.

In this post I will not go over all of D3, but I will try to convey its philosophy and style in the hopes that you will have a moment where you just get D3.

#Quick Introduction I have been using D3 for a few years now on projects such as 100 companies and Mihmihi, I have also written tutorials on things like how to draw maps. However, I have never written an introduction to D3 because it is difficult to convey its awesomeness without just using it. So lets get started.

#Data Driven Documents Above all else D3 is a functional toolkit for visualising data, which binds data to visual elements. Using the bound data, the attributes and look of the elements can be defined using utilities and functions. This is the power of D3; it is not visualisation centric, it is focused on data and functions to use that data.

Note: D3 can use any element in the Document Object Model (DOM), such as SVG or HTML elements. This tutorial will use D3 with HTML and CSS (instead of SVG) as more developers are familiar with them.

#Binding Data Lets bind a piece of data to the body element:

javascript d3.select('body').datum('red')

The string 'red' is now bound to body.

Now lets retrieve that datum:

javascript d3.select('body').datum() // 'red'

What can the datum be? Any Javascript thing like objects, string, arrays, and so on. D3 just remembers the relationship between the element and the datum so that it can be used later.

You can use D3 to set attributes on the element you bound the data to:

javascript // changes the body's CSS color to red d3.select('body').style('color', function(datum){ // datum == 'red' return datum } )

You can also chain many of the functions D3 offers for easier use:

javascript // changes the CSS background-color to 'blue' d3.select('body') .datum('blue') .style('background-color', function(datum){ // datum == 'blue' return datum } )

If you have a lot of data then you can select multiple elements with selectAll and bind to those elements with the data function.

Note: Many functions in D3 return a *Selection** including selectAll data and select*

javascript // Selection of all p elements p = d3.selectAll('p') // bind data to the first 5 p elements p.data(['red','green','blue','black','white']) // changes the CSS background-color to bound datum p.style('background-color', function(datum){ return datum } )

D3 also helps if you have more (or less) data than elements that exist. The functions enter and exit are used with append and remove respectively to let you create and destroy elements.

Note: the data function returns a restricted Selection of only elements that can be bound to the data.

javascript // Selection of all p elements in body p = d3.select('body').selectAll('p') // bind data to the first 5 p elements, get restricted Selection pdata = p.data(['red','green','blue','black','white']) // add p elements if they don't already exist pdata.enter().append('p') // remove p elements if there are too many pdata.exit().remove()

#Creating Functions Binding data to elements using D3 is cool, but do you know what is really cool? Functions! D3 has a massive set of Higher order functions (functions that return functions) that let you easily create useful tools to manipulate your data.

Specifically, my favourite functions in D3 are Scales.

For the following example I will use the Scoville scale of how spicy differed peppers are for our example. Here is some data (shu stands for Scoville Heat Unit):

data = [ { name: 'Pimento', shu: 100}, { name: 'Anaheim', shu: 2500}, { name: 'Jalapeno', shu: 10000}, { name: 'Cayenne', shu: 50000}, { name: 'Habanero', shu: 350000}, { name: 'Ghost', shu: 1500000} ]

##Quantize Scales

I will start by creating a function to describe this data with words like 'medium' and 'hot'. For this the 0 to 1,500,000 SHU needs to be converted to words using a Quantize Scale that has a continuous domain (input) and an discreet range (output).

The first thing to notice is that the SHU data looks exponential, so to make it useful for other functions a Log Scale can be used.

logscale = d3.scale.log() .domain([1,1500000]) .range([0,1])

This scale creates a function that takes a number from 1 to 1,500,000 and returns a number from 0 to 1 on a log curve.

Note: This is not a very precise scale and you could tweak the numbers to get what you needed, but for this example it is good enough.

Now lets define the list of words using a quantize scale:

qscale = d3.scale.quantize() .domain([0,1]) .range(['nothing', 'mild','medium','hot' ,'burning','scorching','hellfire'])

This function takes a number from 0 to 1 and returns an element from the list of words provided. For example, qscale(0) will return 'nothing' and qscale(1) will return 'hellfire'.

To be useful a function that combines and maps to the data provided is needed:

textscale = function(datum){ return qscale( logscale(datum.shu) ) }

Text scale will then take a pepper object and return a word that matches it hotness. To check the results map can be used:

data.map(textscale) // ["medium", "hot", "burning", "scorching", "hellfire", "hellfire"]

##Linear Scales

Some places use a number of icons visualise how hot something is, e.g. 0 to 4 chilis. A Linear Scale can be used for this:

linearscale = d3.scale.linear() .domain([0,1]) .range([0,4])

This scale takes a number from 0 to 1 and returns a number from 0 to 4 on a linear line, e.g. linearscale(.5) returns 2. However, it may return non-integer numbers, so when creating the mapping function it should round the output.

chiliscale = function(datum){ return Math.round( linearscale( logscale(datum.shu) ) ) } data.map(chiliscale) // [1, 2, 3, 3, 4, 4]

##Color Scales

Another way to visualise how spicy a pepper is, is to use color. One of the best features of D3 is that it lets you use hexadecimal colors in the ranges of the scale functions. Here is how you can use a linear scale to define color:

colorscale = d3.scale.linear() .domain([0,1]) .range(['#FFF', '#933'])

This scale takes a number from 0 to 1 and returns a color from white to red-ish. Again, a simple mapping function is needed:

burnscale = function(datum){ return colorscale( logscale(datum.shu) ) } data.map(burnscale) // ["#debdbd", "#c78f8f", "#bd7b7b", "#b16464", "#a34848", "#993333"]

#Adding Visual Elements Using the tools I discussed earlier, lets create a simple visualisation for the peppers data-set.

First lets create some divs for our peppers:

peppers = d3.select('.peppers').selectAll('div') pepperssdata = peppers.data(data) .enter().append('div').attr('class','pepper')

Note: I added the class pepper is for some styling

Using the text function, the text inside of the div can be set to the name of the pepper:

pepperssdata.text( function(datum){ return datum.name } )

A div is added with the class how-spicy with text assigned by the textscale function:

pepperssdata.append('div').attr('class','how-spicy').text( function(datum){ return 'is ' + textscale(datum) } )

The background color is set by the burnscale function:

pepperssdata.style('background-color', burnscale)

This is the most complicated bit of code, but contains nothing that has not been seen before:

pepperssdata.append('div').attr('class','fire').selectAll('i') .data( function(datum){ return new Array(chiliscale(datum)) } ) .enter().append('i').attr('class','fa fa-fire')

First this appends a div with class fire, then selects all the i elements (there are none yet). Then by passing to data a function that creates an empty array the length of chiliscale, a new set of data is created for each pepper. Finally, it appends an i element with the icon classes for each piece of data in the empty array.

Note: Unfortunately, Font Awesome does not have a chili icon (see here)

Add some CSS and:

View code here

This is not the prettiest, or very useful, or the most interesting visualisation in the world. It is however a good start to learning D3.

#Conclusion D3 is a powerful tool, and this post has barely scratched the surface. If you want to learn D3 I recommend finding some data that you are interested in, then making a small visualisation for it. D3 is definitely a tool that you learn by using, so go and use it.

#Read More

Every so often I go over to bl.ocks and have a look at what Mike Bostock (the creator of D3) is building. You can get some amazing D3 examples from there.

Visual Display Quantitative Information -- Edward Tufte

Getting Started with D3 -- Mike Dewar

Related Posts

comments powered by Disqus