Hi, I'm Nicholas Johnson!

software engineer / trainer / AI enthusiast

D is for Data

D3 is for Data Driven Documents

By Nicholas Johnson

Document Version:

Last Updated:

Workbook Cover Image

Welcome to D3

D3 is an extremely interesting data visualisation library by Mike Bostock.

It provides you with a sensible suite of tools to quickly produce any kind of chart you can imagine. D3 does not limit you to specific chart types, it allows you to create elements in response to data. This is where it gets its name D3 - Data Driven Documents.

Selections

D3 allows you to select and manipulate elements on the page as you would expect.

Rather wonderfully, D3 allows you to select and manipulate elements that are not yet on the page, but should be, and also elements that are on the page, but which are no longer needed.

When your data changes, D3 provides you with virtual selections containing the DOM nodes you should add.

Transitions

D3 provides you with a toolkit for adding animation to your visualisation. You can morph between two datasets for example, or make an element grow.

The following code will transition an element from 0 to 100 units high over 1000 ms.

d3.select("rect")
  .attr("height", 0)
  .transition()
  .duration(1000)
  .attr("height", 100);

Function chaining

Everything in D3 is achieved using function chaining. We create, modify and configure objects and selections by chaining functions one after the other. This gives a clear logical flow of information through the system.

Scales

Scales are helper functions that allow us to map data into coordinate space in our graph.

All the work of working out how tall a bar should be to fit the space, or where a label should be drawn is extracted into a scale function. Change the scale and everything changes.

AJAX

D3 comes with a sensible AJAX library for pulling data from the internet. You can pull and automatically parse XML, CSV, TSV and JSON.

There’s also support for mapping arbitrary document formats to JSON.

Layouts

Layouts are friendly helper functions for transforming data into formats that are easy to display.

For example:

d3.layout.pie();

will provide you with a function that will map an array of values into an array containing start and end angles for a pie chart.

SVG

D3 will let you create any HTML you like, but it works best with SVG.

SVG is a library that lets you draw vector graphics right into your web page. It works in all modern browsers down to and including IE9.

D3 contains many useful functions to make working with SVG a joy forever.

Select

D3 gives you a selection library, a lot like jQuery. We can call:

d3.select("div");

This will return a selection containing the first div on the page by CSS. Typically you want something a little more unique though, such as:

d3.select(".graph");

This gives you an array containing a single element. If nothing matches, you get back a selection containing a null value.

selectAll()

We can also select multiple elements at once. We can call:

d3.selectAll(".graph div");

This will select every div inside every element with a class of ‘graph’. Just like jQuery, we get back an array containing all of the matching DOM nodes.

If nothing matches you get back an empty selection.

%aside

Null elements

select() will return a match containing null if no element matches. selectAll() will return an empty selection. D3 will generally ignore empty values when applying attributes or appending children.

Selection chaining

We can of course also chain selections together. Say we wanted to match the divs within a .graph:

d3.select(".graph").selectAll("div");

We might also break this chain down into parts like this:

let graph = d3.select(".graph");
graph.selectAll("div");

Thus far D3 is quite similar to jQuery.

Exercise - selections

Given the following HTML:

<!DOCTYPE html>
<html>
  <head>
    <title></title>
  </head>
  <body>
    <h1>Title</h1>
    <div class="graph">
      <div></div>
      <div></div>
      <div></div>
    </div>
  </body>
</html>
  1. Select the h1.
  2. Select the graph element.
  3. Select all the divs inside the graph, there should be 3.

Append and Attr

We can use D3 to add elements and attributes to our DOM.

Having selected an element or elements, we can use append to add an element to it, like so:

  d3.select('body').append('div');

Attr()

We can set attributes on any element using the attr function:

  d3.selectAll('div').attr('title', 'Dave the div');

Style()

It is common to want to set the style attribute of an element. We can do this using the .style() function.

  d3.selectAll('div').style('color', 'red');

You can set several styles at once by passing a JSON object.

  d3.selectAll('div').style({color: 'red', background: 'red');

Text()

We can also set the text content on any element using the text function:

  d3.selectAll('div').text('Hello there!');

Remove()

We can also remove elements from the page using .remove()

  d3.selectAll('div').remove();

We will need to make use of all of these functions when we start making charts.

Easy Exercise - Add an element

  1. Use the append function to add an a tag to the page.
  2. Use the style function to make it big and red.
  3. Use the attr function to set the href attribute and point it somewhere inappropriate.

Harder Exercise - Hand code a chart

We can create a simple bar chart out of styled divs. We just need to give the divs a style element that specifies a background, width and height.

  1. Use D3 append to create several divs on the page, one at a time. Use the style function to set a different width on each of them.
  2. Use the text function to add a label to each div.
  3. Now select all the divs using selectAll and use the style function again to set a height and background colour.

You should be able to accomplish this in around 10 lines of JavaScript.

Your final chart should look something like this:

20%
40%
80%
60%
90%

Data

D3 gives us a function called data. The data function operates on a selection and receives an array of information. It then maps each element in the array to the elements in the selection array.

Each element in the selection gains a new attribute called data. This attribute is added directly to the DOM node, not as an HTML5 data attribute.

Data is stored on the DOM node

The data is stored as an attribute of the DOM node object, not as a data attribute in the html. You will need to use the inspector to see it.

Given html like this:

<div class="graph">
  <div></div>
  <div></div>
  <div></div>
</div>

The following JavaScript will add a an attribute called data to each div node. This will not be visible on the page, you’ll need to use your inspector to inspect the DOM node itself.

d3.select(".graph").selectAll("div").data([1, 2, 3]);

Note that there are three divs and three elements in the data array. The first div gets data:1, the second data:2 and the third data:3.

‘Aha’ I can hear you asking, ‘What happens if there are more divs than data, or vice versa?‘. All in good time.

Attributes from data

After we have bound data to a node we can access it in various ways. Let’s set an attribute.

We can receive this in a function, and use the return value of the function as the value of the attribute.

Here we use the style function to set a style attribute. Notice that we have passed a function as the second parameter to the .style() function. This function receives the bound data, and also i, which is an iterator.

d3.select(".graph")
  .selectAll("div")
  .data([1, 2, 3])
  .style("width", function (data, i) {
    return data * 100 + "px;";
  });

This will give us a page like this:

<div class="graph">
  <div style="width:100px"></div>
  <div style="width:200px"></div>
  <div style="width:300px"></div>
</div>

Text

We can also create text elements from data, again by receiving a function. This function can receive data, and also i which is the current iterator.

d3.select(".graph")
  .selectAll("div")
  .data([4, 5, 6])
  .text(function (data, i) {
    return i + " : " + data;
  });

This will give us a page like this:

<div class="graph">
  <div>1: 4</div>
  <div>2: 5</div>
  <div>3: 6</div>
</div>

Exercise

Start with html like this:

<div class="graph">
  <div></div>
  <div></div>
  <div></div>
</div>

use data like this:

let data = [10, 60, 25];
  1. Select the divs using selectAll().
  2. Bind the data to the selection using data().
  3. Check in your inspector, see if you can find the data on the element.
  4. Use the style() function to set a width on the elements based on the bound data. You will not need to do any work to make this happen, just pass a function to the style function, have it receive data, and return a width.
  5. Use the text() function to set text.

Your final chart should look something like this:

%div{ style: ‘padding:10px; width:10%; margin:1px 0; color:white; background:red’} 10% %div{ style: ‘padding:10px; width:60%; margin:1px 0; color:white; background:red’} 60% %div{ style: ‘padding:10px; width:35%; margin:1px 0; color:white; background:red’} 35%

Further Exercise - Sorting

Your data will look nice if it is sorted. Read up on Array sorting here:

https://github.com/mbostock/d3/wiki/Arrays

Create a larger data set by typing random numbers, now use the array sort method to sort the data prior to display

Very cool.

Enter and exit

In the example above we created a div for each element in the array. our selection was the same length as the array (3).

But what happens if the length of the selection is different to the length of the array? It would be handy to be able to create new DOM nodes automatically, or remove nodes that are not needed.

The data() function returns an object which contains two sub-selections called enter and exit. We can get at these using the enter and exit functions.

The enter function returns a virtual selection containing all of the data that isn’t currently on the page.

The exit function gives us a selection containing all of the nodes in the selection that are not needed given the data.

Enter()

The enter function gives us access to a list of placeholder nodes that correspond to the data that is not currently bound to the selection.

If we append to the enter() selection, nodes will be created, as many as are needed to satisfy the selection.

Starting with html like this:

<div class="graph"></div>

We can append divs to the enter selection.

bars = d3.select(".graph").selectAll("div").data([1, 2, 3, 4, 5, 6]);

bars
  .enter()
  .append("div")
  .style("width", function (data) {
    return data * 10 + "%;";
  });

We will get a page that looks like this:

<div class="graph">
  <div style="width:10%"></div>
  <div style="width:20%"></div>
  <div style="width:30%"></div>
  <div style="width:40%"></div>
  <div style="width:50%"></div>
  <div style="width:60%"></div>
</div>

We can chain another style function to get the height and background, or just use CSS.

Exit()

The exit selection corresponds to nodes that are no longer needed. Say you rebind data to the selection, any nodes that are not needed to represent the data can be removed.

Given the example above, say we bound a shorter array of data, we could remove the superfluous divs using the remove function on the exit selection.

let bars = d3.select(".graph").selectAll("div").data([1, 2, 3]);

bars.exit().remove();

Exercise - Draw a graph

Basing your answer on the code above, given a data array like this:

[10, 20, 50, 40, 90, 75];

Use data binding and the enter function to add divs to the page and style them into a nice histogram.

SVG (Scalable Vector Graphics)

SVG is a format that has been around for quite a long time, but which was only rolled into Internet Explorer in version 9.

At time of writing, approximately 95% of the UK population can view SVG data.

Check out current browser support at caniuse.com: http://caniuse.com/#feat=svg

Embedding of SVG

SVG data can be embedded. If you have an SVG file, you can embed it in your page using a regular img tag:

<img src="hello.svg" />

Inline SVG

You can also include SVG right in your page with the rest of your markup.

The following will draw a black circle with a radius of 90 units.

<svg width="200" height="200">
  <circle cx="100" cy="100" r="90" />
</svg>

%svg{width: “200”, height: “200”} %circle{ cx:“100”, cy:“100”, r:“90” }

Squares

You can draw squares and rectangles too:

<svg width="200" height="200">
  <rect x="10" y="10" width="180" height="180" fill="red" stroke="black" />
</svg>

%svg{width: “200”, height: “200”} %rect{ x:‘10’, y:‘10’, width:‘180’, height:‘180’, fill:‘red’, stroke: ‘black’ }

Text

You can use SVG to write text directly to the screen.

<text x="20" y="50"> Hello SVG!</text>

%svg{width: “200”, height: “100”} %rect{ x:‘10’, y:‘10’, width:‘180’, height:‘80’, fill:‘red’, stroke: ‘black’ } %text{ x:‘20’, y:‘50’ } Hello SVG!

CSS

If your SVG is on the page you can style it directly with ordinary CSS.

Note. I’m inlining the CSS here for brevity. CSS should go in it’s own file.

<style>
  rect.my_rectangle {
    fill: green;
    transition: 1s;
  }
  rect.my_rectangle:hover {
    fill: red;
  }
</style>

<svg width="200" height="200">
  <rect class="my_rectangle" x="10" y="10" width="180" height="180" />
</svg>

%style rect.my_rectangle { fill: green; transition:1s; } rect.my_rectangle:hover { fill: red; }

%svg{ width:‘200’, height:“200” } %rect.my_rectangle{ x:‘10’, y:‘10’, width:‘180’, height:‘180’ }

Exercise - A hand drawn SVG histogram

Use simple SVG to hand draw a nice bar chart on the page. You can do this vertically or horizontally. Position text elements to show the values.

We will use this markup in the next section.

A hand drawn SVG scatterplot

Use circles to mock up a simple scatterplot. A scatterplot can show up to four data values. Each circle should have an x and a y position, a radius and a colour.

We will use this markup in the scatterplot section.

SVG Histogram

SVG is ideally suited to data visualisation. It is mathematical, and easily constructed with JavaScript. Because the data lives in the DOM we can perform animation by changing attributes.

Appending SVG

We use the D3 append function to append svg elements to the page.

We define an initially empty set of rects which we bind to the data using the data function.

This allows us to call enter to get a list of all rects that should be created, and exit to get a list of all rects that should be removed.

For example

The following code generates a simple stacked histogram:

// First we create an SVG element
let graph = d3
  .select("body")
  .append("svg")
  .attr("width", 200)
  .attr("height", 200);

// Now let's define a function to draw the data
let draw = function (data) {
  // We grab our bars and map to data.
  // No bars exist yet, so this selection
  // defines an enter array which lists
  // the datapoints that need to be mapped.
  let bars = graph.selectAll("rect").data(data);

  // Add any missing bars
  bars
    .enter()
    .append("rect")
    .attr("height", 10)
    .attr("y", function (d, i) {
      return d * 11;
    });

  // Remove any superfluous bars
  bars.exit().remove();

  // Style all bars
  bars.attr("width", function (d) {
    return d * 20;
  });
};

// Finally use the function
draw([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

Exercise

  1. Enter the above code and try to get it working.
  2. Open up a console and call the draw function passing in your own data.

Adding text

Use text elements to add text to the end of each bar.

Fun with Histograms!

Further exercise

Try to modify the code to create a histogram with vertical bars next to each other.

SVG Scatterplot

There’s no rule which says the data you graph has to be one dimensional. Very often you will find yourself working with two dimensional data sets.

Two dimensional data

This might take the form of an array of arrays like this:

let data = [
  [1, 2, 3],
  [3, 2, 1],
];

It might take the form of an array of objects, like this:

let data = [
  { a: 1, b: 2, c: 3 },
  { a: 3, b: 2, c: 1 },
];

You can work with two dimensional data just as easily as with one dimensional data.

Binding two dimensional data

We bind our data in the usual way.

let bars = d3.select(".graph").selectAll("rect").data(data);

Each div now has a data attribute that contains the array or the object. We can access it in the usual way, with the caveat that the data is an object so we use the dot notation:

bars.attr("width", function (data, i) {
  return data.a * 10;
});

Exercise - A Scatterplot

Use the following data set:

let data = [
  {
    dynamism: 8,
    synergy: 6,
    confluence: 42,
  },
  {
    dynamism: 4,
    synergy: 8,
    confluence: 13,
  },
  {
    dynamism: 10,
    synergy: 9.5,
    confluence: 56,
  },
];

We are going to create a scatterplot. A scatterplot has circles arranged along the x,y axis. In addition the circles can be differnt sizes to indicate importance.

You might like to graph dynamism and synergy on the x,y axis, then use confluence to control the spot size. See if you can see any patterns.

Extension

Add more data to the dataset. The plot can handle it.

Massive Bonus Exercise

Attempt to swap the axes. The simplest way to do this will be to create another drawing function that simply redraws the graph using a different set of axes.

Call this function from the JavaScript console.

Transitions

We can tell the DOM nodes in a selection to transition gradually from one state to another using the transition function.

Transition()

Adding the transition function to your chain simply tells the chart to animate all of its attributes.

let bars = d3.select("svg").selectAll("rect").data(data);

bars.transition().attr("width", function (d) {
  return d * 10 + "%";
});

It’s a simple and lightweight solution.

Duration()

We can change the length of our transition using the duration function. Set the duration in milliseconds.

bars.transition().duration(1000).attr("width", "100");

Ease()

You can specify an easing function. The default is ease-in-out.

You might like to try:

  • linear
  • elastic
bars.transition().ease("linear").attr("width", "100");

Delay()

You can make your transitions happen one by one using the delay function. You can pass this function a function, allowing you to stagger the transition start times:

bars
  .transition()
  .delay(function (d, i) {
    return i * 100;
  })
  .attr("width", "100");

Exercise

Revisit your bar chart and apply a transition to it so the bars warp in slowly one by one.

Exercise - Scatter plot

Revisit your scatter plot and apply an animation here too. Have the dots pop in dramatically. A little delay will make things look extra interesting.

Experiment with linear and elastic easing functions.

For a further challenge, try to sort the data from left to right so the dots pop in in order.

Optional Exercise Extension

Use the on() function to hook up a pair of buttons button that will swap the data set. You’ll need to extract your drawing code into a function that receives data. Then hook the buttons up to the drawing fuction and pass in data.

Read about the on function here:

https://github.com/mbostock/d3/wiki/Selections#on

If you’ve covered closure in the JavaScript course, bonus points for wrapping your mini-app in a self executing function.

Events

We can listen out for user events using the d3 on() method.

Say we have a list of rect elements, we can listen for an event and assign a callback, just as we might with jQuery.

Unlike jQuery though, we have access to the data for that element, and also the iterator.

You could use this to show a label on click for example.

d3.selectAll("rect").on("click", function (data, i) {
  // do something here
});

Event types

Many event types are supported including:

  • “click”
  • “mouseover”
  • “mouseout”
  • “submit”

This

We also have access to the clicked on element via the function’s “this” variable. This will contain a plain DOM node, which we can then convert to a D3 selection by passing it to the d3.select function.

d3.selectAll("rect").on("click", function (data, i) {
  let rect = d3.select(this);
  rect.attr("fill", "red");
});

We can then apply any function we like.

Getting the mouse position

We can get the mouse position for an event using the d3.mouse function.

let mousePos = d3.mouse(this);
// returns x,y coords [25, 16]

Exercise - Display different data on click

Create 3 little button elements (you can style these nicely if you like). Use the on() function to listen out for a click. When a click is detected, redraw your favourite chart using a different data set.

Bonus points for animation.

Further Exercise - Highlight a segment

When a click is detected on an element in your graph, highlight it by displaying the data values next to it.

You can display these values using a text element in the SVG, or in a div underneath the SVG element.

Scales

Up till now we have been munging our data into shape manually. If we have data in the range 0 to 10, we have simply multiplied the value by 10 to get a percentage.

Instead it would be better if we could fit our data into a range. We can achieve this with a d3 scale function to normalise our data within a range.

The d3.scale object

d3.scale is an object full of scale function generators. Open it up in a console and you can see all the scales available to you. These are:

  • identity
  • linear
  • log
  • ordinal
  • pow
  • quantile
  • quantize
  • sqrt
  • threshold

Linear scale

We can create a simple linear scale using the linear function:

let scale = d3.scale.linear();

Once made we can pass values to it:

scale(5); // returns 5
scale(99); // returns 99

Domain

We can make this more useful by setting a domain. A domain is the minimum and maximum values the data can have.

let scale = d3.scale.linear().domain([0, 10]);

This will now normalise data between 0 and 1:

scale(0); // returns 0
scale(5); // returns 0.5
scale(10); // returns 1

Setting the domain automatically with min and max

We can set the domain automatically with min and max. Min will give us the lowest value in a data set. Max will give us the highest:

let data = [5, 7, 6, 3, 6];

let min = d3.min(data); // returns 3
let max = d3.max(data); // returns 7

let scale = d3.scale.linear().domain([min, max]);

Now when we call this function, 3 will be the lowest value, and 7 will be the highest:

scale(3); // returns 0
scale(7); // returns 1
scale(6); // returns 0.75

Setting a range

Our data so far has been normalised between 0 and 1. We can normalise to a different range that corresponds to the size of our graph:

Say we make an svg element like this:

let width = 300,
  height = 200;

d3.select("body").append("svg").attr("width", width).attr("height", height);

We can modify our scale function to normalise our data to fit in this graph using the range function:

let scale = d3.scale.linear().domain([min, max]).range([0, width]);

scale(3); // returns 0
scale(5); // returns 150
scale(7); // returns 300

Padding with scales

We can use scales to add padding to our SVG element so the data points are not hard against the edge. Create a padding variable and then use it to adjust your range:

let padding = 50;

let scale = d3.scale
  .linear()
  .domain([min, max])
  .range([padding, width - padding]);

Inverting your chart

You can also invert a chart using scales, just swap the range values over:

let scale = d3.scale.linear().domain([min, max]).range([width, 0]);

Exercise - Add a scale to your graph

Choose your favourite graph and modify it to work with a scale. Add a little padding using a padding variable so your elements don’t press against the edge.

Exercise - Invert your graph

Use a scale to invert your graph. Notice how this simplifies quite a lot of the maths to do with positioning elements.

Exercise - Log Scale

Modify your graph to use a log scale. This involves changing a single word in your code. Nice eh?

Colour Scales

We can also create a scale which will automatically generate a colour based on our data value.

The colour scales are:

  • category10
  • category20
  • category20b
  • category20c

category 10 contains 10 colours, the rest contain 20 colours. These colours have been specially designed to be harmonious and attractive.

Generating a colour scale

We can make a colour scale by calling one of the category functions:

let colour = d3.scale.category20();

We can now make a colour simply by calling the colour function:

let bars = d3.selectAll("rect");

bars.attr("color", function (d, i) {
  return colour(i);
});

Exercise - assign colours to your chart

Use the colours function to automatically assign colours to one of your charts.

Axes

Axes are a common feature of graphs, so D3 gives us a helpful library of features to help us generate them.

Assuming we have a scale called xScale, we can generate an axis using something like:

let xAxis = d3.svg.axis().scale(xScale);

Plotting the axis

The axis is complex, consisting of many paths and text elements, so we need to keep it together. The easiest way to do this is with a group

let graph = d3.select("body").append("svg");

graph
  .append("g")
  .attr("transform", "translate(0, " + (height - 30) + ")")
  .call(xAxis);

Limiting the ticks

You might find your axis is too dense to read. You can make it a little more same using the ticks function:

let xAxis = d3.svg.axis().scale(xScale).ticks(4);

D3 is smart about allocating the ticks. It will try to pick whole numbers where possible.

Moving the axis about

We can move the axis and everything inside it by moving the group. We do this with the transform attribute.

This attribute allows us to move, scale, skew or rotate the coordinate space within the group. Here we will translate down by 300 units:

graph.append("g").attr("transform", "translate(0, 300)").call(xAxis);

If we are clever we will have variables set up for width, height and padding. This will help us be consistent with our axes and data.

Exercise - Draw an axis on your chart

Pick your favourite chart. Draw an x axis based on the xScale.

For bonus points, set up variables for width, height and padding. Use these to set up the scale on your graph. This will automatically shift everything around based on your data.

Padding gives you space to place your axis. Shift your axis into position using your height and padding variables.

Remember you can still see SVG elements that have fallen out of the viewport using Chrome inspector.

Exercise - Draw a vertical axis

Create a vertical axis. You can do this by appending .orient(“left”) to your axis function.

Draw it in your SVG and try to move it into position.

Further Exercise - Check out the API

The Axis API is rich and extensive. Read up on it here:

https://github.com/mbostock/d3/wiki/SVG-Axes

Have a play and see if you can produce something beautiful.

Advanced exercise - Axis transitions

It is possible to apply transitions to an axis when the data changes. Check out the chained transitions example here:

http://bl.ocks.org/mbostock/3903818

Extend the events exercise from earlier and have a go at transforming the axis in response to the data.

This is going to involve some tinkering.

AJAX

Ajax stands for:

  • Asynchronous
  • JavaScript
  • And
  • XML

It is the process whereby we pull data from a remote server via JavaScript.

Because accessing the network takes time, AJAX is asynchronous. We make our AJAX call and pass a callback function. This function will be called when the request is complete.

We can pull a JSON feed from the Internet something like this:

d3.json("resource-path.json", function (error, data) {
  drawGraph(data);
});

Once the AJAX request has completed, the callback function will be invoked and passed the error (if there was one), and the data that was returned from the server.

We then pass this data to our graph drawing function.

Working on a server

You won’t be able to make AJAX requests when you serve your HTML directly from your file system. This would be a security risk and the browser will block it.

In order to make requests you will need a server.

You can create a simple local server with Node http-server

  npm install http-server -g

Start it with

  http-server

Your files will now be accessible on:

http://localhost:8080

The Same Domain Policy

One of the big problems with AJAX is the same domain policy. This policy says that you are not allowed to make requests outside of your own domain.

There are a couple of ways to get around this:

CORS

Cross Origin Resource Sharing (CORS) is the method natively supported by D3. This is a philosophical decision. CORS is superior technology and we should support it.

Unfortunately CORS requires a header to be set on the server marking the API as open. If you don’t have access to the server, this might be a problem.

JSONP

JSONP works cross browser. JSON ignores AJAX and simply embeds a script tag on the page containing the API call result. It’s a functional hack.

There is a JSONP plugin for D3 available here:

https://github.com/d3/d3-plugins/tree/master/jsonp

Exercise - The Weather

You can pull a JSON feed containing a 10 day weather forecast from here:

http://api.openweathermap.org/data/2.5/forecast/daily?lat=35&lon=139&cnt=10&mode=json

This feed implements CORS, so as long as we are hosting our HTML on a local server we should be able to access it.

Create a directory to hold your index.html and JavaScript. Start a server in that directory using http-server.

Use AJAX to pull the feed and plot the resulting data set in whatever format you see fit. Bonus points for imaginative solutions.

Optionally add scales and axes.

Exercise - Your own data

If you have one, use AJAX to pull your own data feed from the internet.

If you don’t have CORS headers available, you could use the JSONP plugin, or you could download the content and host it locally with http-server alongside your index.html.

Parsing Data

D3 comes with built in parsers for most well formed text based data formats. You will get back a JSON object.

CSV

We can parse CSV using the csv.parse function:

d3.csv.parse("'a', 'b', 'c'\\n 1, 2, 3");

We will get back an object containing strings like this:

{
  a: "1",
  b: "2",
  c: "3"
}

Notice how all the values are strings.

Exercise - parsing

Take an Excel spreadsheet and export it as a CSV file. Now host this locally and use AJAX to load it.

You can use the d3.csv function to load CSV.

Parse it and plot the result in a format that makes sense to you.

Paths

We can draw arbitrary paths using SVG. The syntax for this is a little obscure, but it merits some understanding.

Path

We create a path using the element. This has an attribute d which defines the path. D uses a series of letter codes to tell the cursor how to move around.

Straight lines

The following will draw a 50 px horizontal line.

<svg width="300" height="300">
  <path d="M 10 10 h 50 " fill="transparent" stroke="black" />
</svg>
  • M x y - Move
  • L x y - Line
  • H x - Horizontal line
  • V y - Vertical line
  • Z - Close a path from wherever you are

Here’s a square:

<svg width="300" height="300">
  <path d="M 10 10 h 50 v 50 h-50 z" fill="transparent" stroke="black" />
</svg>

Curves

We can also draw bezier curves:

<svg width="300" height="300">
  <path d="M10 10 C 20 20, 50 20, 200 10" stroke="black" fill="transparent" />
</svg>

We specify:

  1. Control point one
  2. Control point two
  3. Final point

Arcs

Finally we can draw arcs. The key thing to remember with arcs is they have a fixed start and end point. The radius will be adapted to conform to these points.

<svg width="300" height="300">
  <path
    d="M 30 50 A 40 50 0 0 1 162.55 162.45"
    stroke="black"
    fill="transparent"
  />
</svg>

Exercise - Draw a little house

Let’s do something simple. Please draw a little picture of your house. Use lines and arcs as you see fit.

Pie Layout

In order to draw our pie chart, we need to convert our data into a pie friendly format. We can do this using a pie layout.

let pie = d3.layout.pie();

pie(data);

This will convert our data into an array of objects containing start angles and end angles.

Exercise - create a simple pie chart

Assume a simple array of data like this:

data = [1, 2, 3];

Create a pie chart based on this data.

Experiment with converting this into a ring chart.

Exercise - create a simple pie chart from an array of objects

Assume an array of data like this:

data = [
  { name: "cats", quantity: 12 },
  { name: "mice", quantity: 19 },
  { name: "rice", quantity: 88 },
];

Use a pie layout to create a simple pie chart to display that data.

Optionally AJAX in your own data feed.