Skip to content

Latest commit

 

History

History
372 lines (312 loc) · 10.8 KB

z04-examples.md

File metadata and controls

372 lines (312 loc) · 10.8 KB
layout title permalink
page
Examples
/examples/

There's a lot more to D3! This is just a quick tour of some other stuff D3 has to offer.

Layouts and SVG Helpers

Some of these examples make use of D3's layout helpers.

Some layouts convert our original data into descriptions of the shapes we want to draw. For example, the pie layout converts numbers into arcs (start and end angles for pie slices). This keeps our drawing code simple. Other layouts help us group our data so we can draw useful shapes like stacked stacked or trees.

D3 also provides helpers to make some of the more complex SVG shapes easier to draw. The path helper can build curves that interolate between data points. The arc helper can take the angles generated by the pie layout and draw arcs (pie slices).

A Pie Chart

Let's start out by walking through using D3 to draw a simple pie chart.

First we get our source data:

{% highlight javascript %} var sales = [ { product: 'Hoodie', count: 12 }, { product: 'Jacket', count: 7 }, { product: 'Snuggie', count: 6 }, ]; {% endhighlight %}

We want each product to be represented as a pie slice in our pie chart, which involves calculating the associated angles. We'll use the d3.pie helper for that:

{% highlight javascript %} var pie = d3.pie() .value(d => d.count)

var slices = pie(sales); // the result looks roughly like this: [ { data: sales[0], endAngle: 3.0159289474462017, startAngle: 0, value: 12 }, { data: sales[1], startAngle: 3.0159289474462017, endAngle: 4.775220833456486, value: 7 }, { data: sales[2], startAngle: 4.775220833456486, endAngle: 6.283185307179587, value: 6 } ] {% endhighlight %}

Now we have our data in angles (radians), so we can turn them into something visual. The next tool D3 gives us is the d3.arc which helps to create SVG <path> tags for arcs. This is where we provide all the information relevant to actually drawing, such as the radius size.

{% highlight javascript %} var arc = d3.arc() .innerRadius(0) .outerRadius(50);

// helper that returns a color based on an ID var color = d3.scaleOrdinal(d3.schemeCategory10);

var svg = d3.select('svg.pie'); var g = svg.append('g') .attr('transform', 'translate(200, 50)')

g.selectAll('path.slice') .data(slices) .join( enter => { enter.append('path') .attr('class', 'slice') .attr('d', arc) .attr('fill', d => color(d.data.product)) } );

// building a legend is as simple as binding // more elements to the same data. in this case, // tags svg.append('g') .attr('class', 'legend') .selectAll('text') .data(slices) .join( enter => { enter.append('text') .text(d => '• ' + d.data.product) .attr('fill', d => color(d.data.product)) .attr('y', (d, i) => 20 * (i + 1)); } ); {% endhighlight %}

<div class="info">
  Again, we snuck in a new helper, and it's another type of ordinal scale.
</div>

The <kbd>d3.scaleOrdinal</kbd> helper gives us back a function. This function
takes in values (typically IDs) and gives back a value in its domain. The same ID gets the
same color, and it will rotate through its domain.
apart. We initalize it with <kbd>d3.schemeCategory10</kbd> which is a list of10 colors that are pretty easy to tell apart.

Stacked Bars

One of the most common charts to draw is some variation of stacked bars. These are deceptively complex, because after the first layer, each new layer of bars depends on layout of the previous one.

The data requirements are also different because stacked bars need to have dense data sources. In most graphs, we could omit an empty value because it won't be drawn, but in a stacked layout, that still could affect the layout of the next layer.

Let's start we have sales of our products over multiple days.

Sales
Date Hoodie Jacket Snuggie
2014-01-01 6 2 3
2014-01-02 7 5 2
2014-01-03 8 7 3

Transformed into a dense array, our data looks like this:

{% highlight javascript %} var sales = [ { date: "2014-01-01", hoodies: 6, jackets: 2, snuggies: 3 }, { date: "2014-01-02", hoodies: 7, jackets: 5, snuggies: 2 }, { date: "2014-01-03", hoodies: 8, jackets: 7, snuggies: 3 } ]; {% endhighlight %}

Now we can take advantage of the d3.stack to do the work of stacking our layers on top each other. While normally a bar graph would have one y value, a stacked one has two:

  • where a segment starts ("baseline")
  • where the segment ends

For the first layer stacked bar chart (at the bottom), the baseline is typically 0. It can be other values for things like streamgraphs, which are a whole other topic.

{% highlight javascript %} var stack = d3.stack() .keys(["hoodies", "jackets", "snuggies"]);

var stacked = stack(sales); {% endhighlight %}

Now, stacked will be a set of nested arrays containing the hights of the data in sales, stacked, which will come in handy when it's time to draw these. For examples, the stacked data now looks like this:

{% highlight javascript %} stacked [ [[0, 6], [0, 7], [0, 8 ]], [[6, 8], [7, 12], [8, 15 ]], [[8, 11], [12, 14], [15 18 ]] ] {% endhighlight %}

But the data is not a plain array! It also has a few useful properties. The "rows" have key and index and the computed start/end arrays also have data -- the original data.

{% highlight javascript %} stacked // [Array[3], Array[3], Array[3]] stacked[0] // [Array[2], Array[2], Array[2]] Object.keys(stacked[0]) // ["0", "1", "2", "key", "index"] stacked[0].key // "hoodies" stacked[0][0].data // {date: "2014-01-01", hoodies: 6, jackets: 2, snuggies: 3} {% endhighlight %}

Ok so let's get to drawing! We'll bring back our good friends d3.scaleLinear and d3.scaleTime.

{% highlight javascript %} var height = 200; var width = 200;

// we need to calculate the maximum y-value // across all our layers, so we find the biggest // end value var maxY = d3.max(stacked, d => d3.max(d, d => d[1]));

var y = d3.scaleLinear() .range([height, 0]) .domain([0, maxY]);

var x = d3.scaleTime() .range([0, width]) .domain(d3.extent(sales, d => new Date(Date.parse(d.date)))) .nice(4);

var svg = d3.select('svg.stack'); var color = d3.scaleOrdinal(d3.schemeCategory10);

// bind a tag for each layer var layers = svg.selectAll('g.layer') .data(stacked, d => d.key) .join( enter => { enter.append('g') .attr('class', 'layer') .attr('fill', d => color(d.key)); } );

// bind a to each value inside the layer layers.selectAll('rect') .data(d => d) .join( enter => { enter.append('rect') .attr('x', d => x(new Date(Date.parse(d.data.date)))) .attr('width', width / 3) // remember that SVG is y-down while our graph is y-up! // here, we set the top-left of this bar segment to the // larger value of the pair .attr('y', d => y(d[1])) // since we are drawing our bar from the top downwards, // the length of the bar is the distance between our points .attr('height', d => y(d[0]) - y(d[1])); } ); {% endhighlight %}

<p>There are a few things that make this graph a little more complex. One of
the hardest parts is realizing that D3 is really only going to hint at how
we should stack the bars: D3 gives us stacked results in our data space, but
not in SVG's coordinate system. We have to deal with the same confusing
<a href="{{ "/parts-of-a-graph/#the-scale" | prepend: site.baseurl }}">Y-axis
coordinate flip</a>.</p>

Onward!

D3 has a lot to offer, and our goal here was to give a brief tour and cover some core concepts! There's much more to learn about D3, but hopefully this tutorial has given you enough so that you can teach yourself the rest.

There are lot of great resources for learning D3 out there:

  1. First and foremost, D3's own wiki. This is a great starting point for any D3-related exploration

  2. Nestled inside that wiki, the D3 API Reference is great for remembering what APIs there are and what the various parameters mean.

  3. For more examples of what is possible with D3 check out the D3 examples by creator of D3, Mike Bostock.

  4. The D3 graph gallery is a collection of examples for dozens of different types of charts made with D3. It is maintained by Yan Holtz.

But don't stop there! Google searches are a great way to discover things too. Happy visualizing!

<script type="text/javascript" src="{{ "/javascripts/examples.js" | prepend: site.baseurl }}"></script>