Over the summer, Social Innovation Simulation will be posting a series of technical tutorials designed to acquaint those interested with some of the technologies we’re working with.
This tutorial describes how you can use a data visualization library called D3 (“Data Driven Documents”). D3 provides tools to manipulate data and display it. D3 is useful when you have lots of data and you want to present it to others in a way that will make it easy for them to quickly understand since it provides easy ways of making visual representations of data.
Estimated Time: 2 hours.
Dependencies: Basic HTML and Javascript knowledge
Tips for going through the tutorial:
- Overview of Tutorial
- Basics of d3
- Starting Out
- Selecting and Adding Elements
- Assigning Data to Elements
- Using Your Data
-
For each section in the “Basics of d3” part of the tutorial there is an html reference file that shows what your code should look like at the end of that section entitled “[SectionName].html”
-
The lines in blue are Javascript code. The first section’s code is to be put in a blank HTML document and all other sections should be inserted in the template created in the first section in place of /*d3 CODE GOES HERE*/.
Overview
What is d3 and why use it?
D3 is a data visualization library meaning that it provides you tools to manipulate data and display it. D3 is very useful when you have lots of data and you want to present it to others in a way that will make it easy for them to quickly understand since it provides easy ways of making visual representations of data. D3 is a Javascript library, so it is used in conjunction with regular Javascript code in an html file.
What are Selections and why do I need to assign data?
The d3 language is based on two main concepts: selections and assigning data.
Selections
This is the way that d3 interacts with your html elements. D3 will allow you to select one (or more) html elements through different characteristics:
Characteristic |
Format |
Examples |
Name of element |
<name> |
d3.select(“body”), d3.selectAll(“p”) |
Class |
.<name> |
d3.select(“.car”), d3.selectAll(“.house”) |
ID attribute |
#<name> |
d3.select(“#farm1”), d3.selectAll(“#cold”) |
Other attributes |
[<attr_name>=<name>] |
d3.select(“[fill=red]”), d3.selectAll(“[height=10]”) |
*Note: in Format section replace <name> with the name of the characteristic and <attr_name> with the attribute’s name in the last row
*Note: select() will only select the first element that matches the criteria while selectAll will match all elements that match the criteria
The above characteristics can also be combined for more specific selections. Putting multiple characteristics together will select only elements that match all the characteristics (logical AND) while separating the characteristics by a comma will select elements that match any of the characteristics (logical OR).
Ex1. d3.selectAll(“rect.car”)
-> selects all “rect” elements with the class “car”
Ex2. d3.selectAll(“#farm,.house”)
-> selects any element with either the id “farm” or the class “house”
Assigning Data
In order to use the visualization techniques that d3 offers with your selection you must first assign data to the elements. D3 allows you to assign data to any selection with the data() function, which takes in any Javascript data type. If an array is passed in then each element of the array is used as an individual piece of data and so you can assign many different elements data at the same time.
The selection.data() function performs what is called a “join” between the selection HTML elements and the data values. This will match the HTML elements and the data values together and then let you manipulate the elements based on the data.
When using the selection.data() function there are 3 groups of HTML elements that are created: enter(), exit() and update(). These groups are all based off of the matching that is performed with the elements of the data (any number if data is an array, one otherwise) and the elements of the selection (any number if selectAll() is used, one if select() is used). By default elements are matched by their order (the first element of the selection is matched to the first element of the data, etc.) but a different matching function can be specified. Each group consists of elements of the selection or placeholders for future HTML elements that will be added*. In each case the data that is matched is assigned to that HTML element, if any data matches it. The following table shows the differences between each group:
*The placeholder is for the type of elements specified by the selection, e.g.,
"p" -> <p> "rect.car" -> <rect class="car"> "circle#ball" -> <circle id="ball">
For the examples: data = [1, 3, 4, 6], selection ids = [rect1, rect2, rect3, rect5], the elements are matched based the data value and the id (i.e. 1 matches to rect1)
Group Name |
Description |
Example |
enter() |
A placeholder for all data elements that do not have a matching element in the selection |
[(*placeholder*, 4), (*placeholder*, 6)] |
exit() |
Any elements in the selection that do not have a matching element in the data |
[(rect2, *nothing*), (rect5, *nothing*)] |
update() |
Any elements in the array that do have a match in the dataset |
[(rect1, 1), (rect3, 3)] |
Note: The example column shows an array representing the elements of the group as pairs of the element and the data assigned to it
Once a selection of HTML elements have been assigned data the data can be accessed when modifying any aspects of the element in d3 by using function(d,i){/*RETURN MANIPULATED DATA*/} (where d is the data for the element and i is the index of that HTML object in the selection) for any value in functions on the selection.
Basics of d3
-
Starting out
-
The first thing to do before using d3 is to get your document set up. Since d3 is a Javascript library all the code that we write will be written in an html file within the <script> tag. This means the first thing to do is to open an html file with the basic tags. This code is inserted into a blank document to get set up:
-
<!DOCTYPE html> <meta charset="utf-8"> <body>
-
Next you will need to import the d3 library so that you can call the functions that d3 gives you. This is done by adding the following line in your <body> tag:
<script src="http://d3js.org/d3.v3.min.js"></script>
*Note: you can also copy the d3 library locally and change the src to reference your local version if you prefer
-
Finally you need to add another script tag to write your d3 code in:
<script> /*d3 CODE GOES HERE*/ </script>
-
Selecting elements
-
The first thing we are going to do create an HTML element in d3. To start off we will make a paragraph. To do this we will need to select the <body> tag and append a <p> (paragraph) tag to it.
-
As mentioned in the overview there are 2 ways to select elements in d3; d3.select and d3.selectAll. The first selects only the first element that matches the given criteria and the second selects all of them.
-
Since there is only one <body> tag in our html element so far it doesn’t matter which we use, but we’ll use d3.select to make it easier:
-
d3.select(“body”)
which will return a selection of our <body> tag.
-
We then want to append a <p> tag to our selection using the selection.append(“[ElementName]”) function on our selection. D3 allows you to chain together multiple functions so we can simply add our function after the previous line:
.append(“p”)
This function (any other function that creates elements) will then return a selection of the newly created element so we can modify the <p> element with d3 now without any other code.
-
Next we want to add text to the paragraph. We can use the selection.text(“[Text]”) function to change the text of our <p> element since our last function returned a the selection of our new <p> tag. Again we can just chain these functions together:
.text(“A new paragraph!” )
-
Similar to the selection.append function, this function (and any other function that modifies an elements characteristics) will return a selection of the element so the functions can be chained together as above. We could, for example, chain another function that will change the colour of our paragraph font. To do this we use the selection.style(“[StyleName]”, “[Value]”) function. This function will let us change the value of any style attribute for any HTML element. Since our previous function returned a selection of our new paragraph then we can simply add the following line of code to change the colour:
.style(“color”, “red”)
-
Similarly there is a selection.attr(“[AttributeName]”, “[Value]”) function that allows us to change the values of any attributes of the HTML element. With a <p> tag, for example, we can change the alignment of the text using this function. To make the text centered we add:
.attr(“align”, “center”)
And now we have a centered, red line of text entirely created with d3!
-
Assigning Data to Elements
-
While creating and selecting elements in d3 is all well and good this is nothing new for Javascript code. The real power of d3 comes when you start using data with your HTML elements. The first step to this is to create some data. We will use a simple array to start off:
-
var dataset = [1,5,8,3,6]
-
Next we have to create some elements to assign the data to. To start off we will make some bars using <div> tags. Just like in the previous section we are going to append elements to the body section but we’re going to do it a bit differently this time. The first step is to select all the “div” elements. This may seem odd since there are none yet but this lets d3 now what we are going to be making with our data:
d3.select(“body”).selectAll(“div”)
-
Next we are going to join our data with the <div> elements. Again this may seem weird because there
.data(data) .enter()
This code will now return a selection of placeholder values of divs (one for each element in our data array) each with an element from the dataset assigned to it.
-
Now that we have our placeholders we have to fill them with HTML elements:
.append(“div”)
This is just like the last time we added HTML elements but with one difference; the function is applied to each element in the selection. This is the same when we call any function on a selection with more than one element and saves us a whole lot of work. So after calling this function we will have one <div> element for each data element in our dataset.
-
Finally we have to add some attributes to our <div> element, using the same functions as before:
.style(“height”, “40px”) .style(“width”, “10px”) .style(“background-color”, “red”)
and just like before this will apply these changes to every element in the selection (saving us a ton of work). And if you run your code now you should see 5 red rectangles just like this:
Even though that looks like one big rectangle its actually 5 small ones side by side. I swear. And if you don’t believe me you can inspect the element on your browser to see for yourself.
-
Using your Data
-
In this section we are going to be working off of our code from the previous section.
-
If you notice in the last section, when we specified the attributes of the <div> elements we used hard-coded strings (“40px” or “red”). Another way that we could do this is using an anonymous function that returns the value that we want. For example we could change
-
.style(“height”, “40px”)
to
.style(“height”, function(){ return “40px”})
-
Woohoo! Now why would we want to write all that extra code? Well for this example we wouldn’t… BUT this will allow us to do some cool stuff with data. Using an anonymous function we can specify parameters which d3 will automatically fill for us. We can specify up to 2 parameters for the anonymous function; the first will always be assigned the data of the selected HTML element and the second will always be the index of the selected element (the index of the first element in a selection is 0, the second is 1, etc.). This lets us use our data to change the attributes and pretty much anything else we want with these functions. For example if we change the
.style(“height”, function(){ return “40px”})
line to
.style(“height”, function(d,i){ return d * 5 + “px”})
we will get this:
What we have done here is instead of specifying what height each rectangle will be explicitly we call a function (on each element) that returns the value of the height. This function takes the data assigned to the element (the “d” parameter) and the index of the element (the “i” parameter) automatically (it cannot take anything else for these parameters). It then evaluates the function for each <div> element in our selection.
-
This is pretty crazy when you think about it since we can use this code for all kinds of different data sets and get different results. Just by changing the data that is assigned to each element and keeping everything else the same we can modify any number of attributes of the element (assuming they are calculated based on the data).
-
In the last example we specified values for both the data and the index but we only used the data value. This is perfectly ok and is a good habit to get into since if we only want to use the index we must specify both parameters. If we did something like
.style(“height”, function(i){ return i * 5 + “px”})
then “i” would refer to the data again, not the index. You can test this out by switching this line for the one we just added and you’ll get the same result when you open it. This is because d3 doesn’t care what you call your function parameters, it just knows that if there is a first parameter then that parameter is filled in as the data and the second parameter is filled in as the index.
-
We can also use the index parameter, just like the data parameter, to modify the elements’ attributes. For example we can change the line
.style(“width”, “20px”)
to
.style(“width”, function(d,i){ return (i + 1) * 5 + “px”})
and get this:
Now we have our data controlling the height of our divs and the indexes controlling the width! The coolest thing about this is that if we change our dataset we can completely change what our page looks like. For example if we changed our dataset to
var dataset = [17, 25, 2, 13, 8, 11, 30]
we would get this:
As you can imagine there is no end to the number of interesting things you can do by just defining your HTML elements’ attributes with your data!
Pingback: Tutorial — Making Maps on D3 | Social Innovation Simulation