Part Two: Scale Functions
If there’s one piece of D3 functionality that I’ve used in just about every data visualisation I’ve built, it’s the scale functions. A scale function takes an input (such as a country’s GDP) and converts it into an output (such as pixel or colour values).
Let’s start with some examples. Suppose we have some data that varies between 0 and 100 and our visualisation is 600 pixels high, then a scale function could convert our data into pixel values for us:
myScale( 0 ); // returns 0 myScale( 50 ); // returns 300 myScale( 100 ); // returns 600
We can even make a scale function to convert our data into colour values:
myColourScale( 0 ); // returns '#dddddd' (grey) myColourScale( 50 ); // returns '#ee6f6f' (pale red) myColourScale( 100 ); // returns '#ff0000' (red)
How to make a scale function
Making a scale function in D3 is pretty straightforward. First answer the following questions:
- what is the minimum and maximum of my data?
- what is the minimum and maximum of the output?
The former is known as the ‘domain’ and the latter as the ‘range’. We can then make a scale function using:
var myScale = d3.scale.linear().domain([-10, 10]).range([0, 600]);
Using scale functions
The most common use of scale functions is for position:
var width = 600; var xScale = d3.scale.linear().domain([0, 10]).range([0, width]);
Given a data set [0, 2, 2.3, 7, 8.5, 10] we can use xScale to determine the x-position each point:
We can configure a scale function to output colours instead of numeric values:
var colorScale = d3.scale.linear().domain([0, 10]).range(['#ddd', 'red']);
We can also configure the scale function to use more than 2 colours. A good example is when we want to display positive and negative values:
var colorScale = d3.scale.linear().domain([-10, 0, 10]).range(['red', '#ddd', 'green']);
When using circles to represent data it’s standard practice to set the circle’s area (rather than radius) according to the data. The maths to figure this out is not rocket science but it’s an unnecessary distraction when building data visualisations. A circle’s area is proportional to the radius squared so to factor out the square we can use D3’s square root scale:
var circleScale = d3.scale.sqrt().domain([0, 10]).range([0, 100]);
Reversing the y-axis
An inconvenience of SVG is that its y-axis points downwards whilst in most charts we want the y-axis to point upwards. A trick to deal with this is to reverse the range of the scale function:
var height = 600; var yScale = d3.scale.linear().domain([0, 10]).range([height, 0]);
I’ve seen people try and deal with the downward y-axis problem without this trick and it soon gets messy!
This article has given you a flavour of the power of D3’s scale functions. They are indispensable when building data visualisations and help keep your code clean, tidy and maintainable. There’s a lot more to scale functions than I’ve covered here but what I have covered should suffice for most visualisations.
For now I’ll leave you with some handy hints:
- try and abstract as much as you can into scale functions. If you find yourself doing algebra inside your D3 code when positioning elements, ask yourself whether you can use a scale function
- use a scale function with a reversed range for handling SVG’s ‘top-to-bottom’ y direction
- use the square root scale function for scaling circle area
Stay in touch
I’ll be publishing more D3 must knows over the coming months and covering subjects such as scale functions, data joins, enter/exit and lots more. If you’d like to be the first to read the next articles then please add your name to my mailing list and you’ll be the first to receive articles as they’re published.