D3 Tutorial

Lucy Havens edited this page Mar 16, 2018 · 23 revisions

Overview

  • Loading Data
  • Selections
  • Binding Data (Joins)
  • Visual Attributes

Example Source: https://bl.ocks.org/d3noob/257c360b3650b9f0a52dd8257d7a2d73

Import D3

  • Option 1 (in the command line): npm install d3
  • Option 2: download the latest release of d3 here
  • Option 3 (load from d3js.org): <script src="https://d3js.org/d3.v5.js"></script>

For local development (options 1 & 2), you must run a local web server. If you haven't installed Node's http-server, to do so in the command line enter:

npm install -g http-server

You can then run the server from the command line by entering:

http-server &

See details on installing and supported environments on the D3 GitHub Wiki.

Setup HTML page

  • put html blurb
  • include d3
  • show where css-styles go (inline, no external file, for simplicity)

Init SVG

D3 data visualizations sit in an svg object. Rather than hard-coding the dimensions, the margin, width, and height variables are useful for reference when positioning the data points and axes of your visualization.

// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

// append the svg object to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
.append("g")
    .attr("transform",
          "translate(" + margin.left + "," + margin.top + ")");

Loading CSV

D3 can load many data file types, including CSV, TSV and JSON. To learn about loading other file types, check out this tutorial.

// get the data
d3.csv("data.csv", function(data) {
    console.log(data);
}

Iterate through data

time format

forEach(e){}

// parse the date/time
// day of the month as decimal number, abbreviated month name, year without century as decimal number
var parseTime = d3.timeParse("%d-%b-%y"); 

// format the data
data.forEach(function(d) {
    console.log(d);
    d.date = parseTime(d.date);
    d.close = +d.close;
});

Linear scales map an input domain to an output range. Essentially, scaling is a normalization process, making sure your data stays within the boundaries of the svg object containing your data visualization.

What's an input domain? The minimum (often 0) and maximum possible values of the data

What's an output range? The possible output display values (often in pixels). This is where the svg height and width variables become useful for defining the maximum range values.

// set the output ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);

// set the input domains
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);

Note: extent is a handy function that returns the minimum and maximum values in a given array using natural order

Selections and Binding Data

A selection is a group (to be specific, an array of arrays) of elements in D3. Similar to JQuery and many other JavaScript libraries, D3 handles groups of elements instead of individual elements one-by-one, as with pure JavaScript. If you're interested in learning about the implementation of selections in D3, check out Mike Bostock's tutorial on How Selections Work.

To create the shapes that will represent each datum, you'll use data joins to bind data to your visualization. Joins to establish the relationship between the shapes in your visualization and the data loaded from your CSV file. The generalization process of joins gives D3 the ability to create dynamic visualizations, adjusting for real-time data feeds, interactions, and transitions. For details on joins (a.k.a. data binding), check out Mike Bostock's tutorial on Thinking with Joins.

First we state what shape we want to bind each datum to. Then we set the properties of the shapes (with methods such as .attr and .style). We'll use an anonymous function to position the shapes based on the bound data, which is typically referenced as as d.

// add the scatterplot points
svg.selectAll("circle")
    .data(data)
  .enter().append("circle")
    .attr("r", 5)
    // position the points
    .attr("cx", function(d) { return x(d.date); })
    .attr("cy", function(d) { return y(d.close); });

Note: we've referenced the columns in the CSV file: d.date and d.close. If the column names had spaces, you'd need to reference them as d["date"] and d["close"].

Add the Axes

In D3, axes are g elements that consist of ticks that have a line element (to draw the tick) and a text element (to label the tick). Scott Murray has a nice tutorial on working with axes in D3 if you're looking for a lengthier explanation. The D3-axis API also explains methods, such as .axisBottom and .axisLeft, which simplify the process of generating ticks.

// add the X Axis
svg.append("g")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x));
// add the Y Axis
svg.append("g")
    .call(d3.axisLeft(y));

Draw the Lines

Similar to axes, D3 has generators for lines that simplify the process of drawing paths between points in our scatterplot. We simply create the valueline variable and position it based on the data we bound to our visualization (referenced in an anonymous function with d, just as with the scatterplot points).

// define the line
var valueline = d3.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });

// add the valueline path
svg.append("path")
    .data([data])
    .attr("class", "line")
    .attr("d", valueline);

Show Distribution (x and y-axis)

Try It Yourself: Can you show a distribution of the points along the x axis?

Try It Yourself: Can you show the distribution of the points along the y axis?

Visual Attributes

What's missing from our scatterplot? We have the data points, we have the x and y axes...what else would a viewer need to understand the purpose of our visualization?

Try It Yourself: Can you increase the size of the points on the scatterplot? Hint: the size of the points in D3 is based on the radius (r)

Try It Yourself: Can you give the scatterplot points, the distribution of points along the X Axis, and the distribution of points along the Y Axis different colors?

Hint: checkout how to use the .style method in D3

Try It Yourself: Can you add text labels to the scatterplot points to show the "date" and "close" values they represent?

Hint: first create the text elements, then use an anonymous function to assign the text elements contents to the data values as demonstrated here

Dynamic Visualizations

One of the reasons D3 is such a powerful visualization tool is its ability to be dynamic. For example, D3 can handle real-time data feeds and user interactions. Let's add an interaction to our scatterplot, creating a tool tip that appears when someone places their mouse over a data point. At the moment our scatterplot shows how the close times change from one date to another, however it does not clearly show the precise close time. By adding tool tips, we can display precise the date and close time for whichever data point someone mouses over.

First, add the changes you'd like to see in the <style> tags:

.point:hover {
  fill: steelblue;
}

div.tooltip {
  position: absolute;
  text-align: center;
  width: 60px;
  height: 28px;
  padding: 2px;
  font: 12px sans-serif;
  background: lightsteelblue;
  border: 0px;
  border-radius: 8px;
  pointer-events: none;
}

Then create the tool tip:

// define the tool tips
var div = d3.select("body").append("div")
    .attr("class", "tooltip")
    .style("opacity", 0);

Lastly, define when the tool tip should appear and disappear for your scatterplot points (the circles):

  .on("mouseover", function(d) {
   div.transition()
     .duration(200)
     .style("opacity", .9);
   div.html(formatTime(d.date) + "<br/>" + d.close)
     .style("left", (d3.event.pageX) + "px")
     .style("top", (d3.event.pageY - 28) + "px");
   })
 .on("mouseout", function(d) {
   div.transition()
     .duration(500)
     .style("opacity", 0);
   });

To create our tool tip, we created an animation using D3's transition interface. Transitions let you define an observable change, an animation that happens over time instead of instantaneously. Transitions are useful because viewers can track the movement of data to understand how it changes, rather than trying to contrast multiple static data visualizations to determine how the data changed from one visualization to another. You can find details about what you can do with transitions here.

Try It Yourself: Can you create a transition that shows how the close time values we're currently displayed change for a new range of dates?

References / Links

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.