D3js (Data-Driven Documents) is a Javascript library that manipulates the DOM and is often used to build SVG plots. I recently created an interactive plot for differential expression data as part of the Tripal Analysis Expression module. Because other Tripal modules use D3 v3.x, I was required to do the same (as opposed to v4, the latest version). While the API is available, I'm the sort of person who learns best from tutorials and examples, and I struggled to find enough v3 examples.
The Tripal Analysis Expression module groups and colors data based on user selection. Each group can be dragged and rearranged. By the end of this series, you'll be able to create higher order plots like this in D3.
With that in mind, I am writing this basic D3js v3 tutorial. This is installment one, where we create a very basic and unimpressive plot using just data()
and enter()
. I recommend some starting resources below, but this tutorial is aimed at beginners.
The code for this lesson is available as a GitHub gist, or visualized with Bl.ocks.
Before we get started, you should understand the basics of
I'd also recommend looking over the D3 website and reading the introduction/gaining inspiration from the plots.
There are other plotting solutions for javascript. I like D3 because it strikes a nice balance between ease of use and flexibility. It might be easier to get results plotting something with, for example, plot.ly (which is built on top of D3), but the higher layer of abstraction leaves you with fewer options.
I recommended reading about jQuery in the introduction because they share the same fundamental goal: select objects in the DOM and manipulate them. jQuery is much broader in scope: it's a general purpose javascript library. D3's twist is revealed in it's name: Data-Driven Documents. The assumption is that you've got data (represented in an array) and you want to apply that data to the DOM. Typically, that means visualize it in some way!
How does D3 communicate your data to the DOM? Using the data()
, enter()
methods!
Let's make a simple example. You can follow along by pasting the code into text editor and opening the file in a web browser.
<!DOCTYPE html>
<style>
.axis .domain {
display: none;
}
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var data = [{name: "one", property: "a", value: 100},
{name: "two", property: "a", value: 50},
{name: "two", property: "b", value: 20},
{name: "four", property: "b", value: 57}, ];
var svg = d3.select("svg")
var bars= svg.selectAll('.bar')
.data(data)
.enter()
.append('g')
.attr('transform', function (d, i) {
return 'translate(' + i* 20 + ',0)';
}).append('rect')
.style('fill', "red")
.style("height", function (d, i) {
return d.value})
.attr('width', 10)
</script>
Below is the resulting plot.
There's a couple of problems here, but let's see what we are trying to do.
name
and value
property.svg
element which holds our graphics.svg
using the data()
and enter()
calls.g
elements to each .bar
object.g
object by translating it, shifting it along the x-axis.rect
element.rect
element based on the data.Pay special attention to function (d, i) {//code}
. Because we've entered our data, we can define a function that takes the i
th element of the data
array as d
as an input, and outputs what we want.
D3 allows for chaining method calls together, much like jQuery. In the above example, we took advantage of chaining several times. For example...
var bars= svg.selectAll('.bar')
.data(data)
.enter()
.append('g')
What's important to understand is that the chain is executed in order, and each link in the chain passes down its selection to the next member. This means that subsequent calls after .append('g')
will affect that g element. On the other hand, setting the style
or attr
of an element will pass on the reference element, meaning you can execute several style
calls in a row on the same element (as we do above)
If chaining confuses you, the above code could be written without chaining, as
var bars= svg.selectAll('.bar')
bars.data(data)
bars.enter()
bars.append('g')
.selectAll().data().enter().append()
This chain will look very familiar to you the more you use D3. The data
method requires a selection to bind data to. It might be confusing that we select all .bar
elements in the above example: what .bar
? They dont exist yet!
That's where .enter()
comes in. It compares the data in data()
and the selection, and creates new placeholder elements for the entered data.
You may be confused how each element gets "told" what its height and x-position should be. The answer lies in how D3 has created an element for each element in our data array.
Consider how we style the height in the above code:
.style("height", function (d, i) {
return d.value})
Rather than returning a fixed height, we use a function to dynamically return the height based on d
and i
. d
is the individual data entry, and i
is its key. In our example code, when i = 2
, d = {name: "two", property: "a", value: 50}
.
Because our data structure has a value
key, simply returning d.value
will set the height of the bar based on the value of that element! If you are getting confused, I recommend including a console.log(d)
in your function. You'll see in your console that each data object in the array is returned one by one.
This is clearly not an award winning plot. We have no axes, scaling, grouping, labels, legends, or titles. The colors are uniform. Oh, and it's upside down (a surprisingly persistent problem with D3!)
We'll cover the rest in later lessons. But for now, try to define your own dataset and bind it to the DOM using data()
and enter()
.
Continue reading with part two: implementing scales.
Did you enjoy this article? Looking for more information? Please feel free to contact me on twitter @bradfordcondon