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.
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).
Let's start out by walking through using D3 to draw a simple pie chart.
First we get our source data:
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:
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.
// 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) .enter() .append('path') .attr('class', 'slice') .attr('d', arc) .attr('fill', function(d) { return 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) .enter() .append('text') .text(function(d) { return '• ' + d.data.product; }) .attr('fill', function(d) { return color(d.data.product); }) .attr('y', function(d, i) { return 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.
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:
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.
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:
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.
Ok so let's get to drawing! We'll bring back our good friends d3.scaleLinear
and
d3.scaleTime
.
// we need to calculate the maximum y-value // across all our layers, so we find the biggest // end value var maxY = d3.max(stacked, function(d) { return d3.max(d, function(d) { return d[1]; }); });
var y = d3.scaleLinear() .range([height, 0]) .domain([0, maxY]);
var x = d3.scaleTime() .range([0, width]) .domain(d3.extent(sales, function(d) { return 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, function(d) { return d.key; }) .enter() .append('g') .attr('class', 'layer') .attr('fill', function(d) { return color(d.key); })
// bind a to each value inside the layer layers.selectAll('rect') .data(function(d) { return d; }) .enter() .append('rect') .attr('x', function(d) { return x(new Date(Date.parse(d.data.date))); }) .attr('width', width / 3) .attr('y', function(d) { // 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 return y(d[1]); }).attr('height', function(d) { // since we are drawing our bar from the top downwards, // the length of the bar is the distance between our points return 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>
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:
-
First and foremost, D3's own wiki. This is a great starting point for any D3-related exploration
-
Nestled inside that wiki, the D3 API Reference is great for remembering what APIs there are and what the various parameters mean.
-
For more examples of what is possible with D3 check out the D3 examples by creator of D3, Mike Bostock.
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>