Chapter 04Continuous Scales
Recall from the last tutorial that continuous scaling functions map a continuous numeric domain defined by an interval to a continuous range defined by another interval.
Continuous scaling functions are created using the following methods:
Each of these methods returns a reference to an instances of one of the following Function object types: continuous, pow, log or time.
Each of these Function object types has a set of methods associated with it that are used to get and set various properties of the scaling function. You can find a comprehensive list in the API documentation. Below is a list of the methods provided by each of the function types.
Example
The example below shows a typical pattern for defining and using a continuous scaling function.
We’ll use the homeless population that we’ve used in some of our previous examples.
var data = [ {"state": "California","population": 134278 }, {"state": "Florida","population": 32190 }, {"state": "Washington","population": 21112 }, {"state": "New York","population": 89503 }, {"state": "Texas","population": 23548 } ];
Rather than displaying a bar graph as we did in previous examples, let’s create 5 circles in a row, one circle for each state, where the radius of a circles is proportional to the size of the homeless population in the respective state.
Since we want to maintain the proportional differences between data points we’ll use the scaleLinear
method. The scaleLinear
method returns a reference to a continuous
object on which we can call the domain
and range
methods to set the domain and range of the scaling function.
Below we create two scaling functions, one to be used when computing the radii and the other to compute where along the x-axis to place the circle.
var rScale = d3.scaleLinear() .domain([0,140000]) .range([1,30]); var xScale = d3.scaleLinear() .domain([0,4]) .range([50,550]);
Below we create 5 circles, set their properties, and create 5 text elements to display the state names. The calls to the scaling functions are in bold font.
var u = d3.select("#homeless1") .selectAll("circle") .data(data) .enter() .append("circle") .attr("cx", (d,i) => xScale(i)) .attr("cy", 50) .attr("r", (d,i) => rScale(d.population)) .attr("fill", "pink"); u = d3.select("#homeless1") .selectAll('text') .data(data) .enter() .append('text') .text((d) => d.state) .attr('x', (d,i) => xScale(i)) .attr('y', 95) .attr('text-anchor', 'middle');
<svg id="homeless1" width="600" height="100" ></svg> <script> var data = [ {"state": "California","population": 134278 }, {"state": "Florida", "population": 32190 }, {"state": "Washington","population": 21112 }, {"state": "New York","population": 89503 }, {"state": "Texas","population": 23548 } ]; var rScale = d3.scaleLinear() .domain([0,140000]) .range([1,30]); var xScale = d3.scaleLinear() .domain([0,4]) .range([50,550]); var u = d3.select("#homeless1") .selectAll("circle") .data(data) .enter() .append("circle") .attr("cx", (d,i) => xScale(i)) .attr("cy", 50) .attr("r", (d,i) => rScale(d.population)) .attr("fill", "pink"); u = d3.select("#homeless1") .selectAll('text') .data(data) .enter() .append('text') .text((d) => d.state) .attr('x', (d,i) => xScale(i)) .attr('y', 95) .attr('text-anchor', 'middle'); </script>
Domain and Range Intervals
The domain and range intervals can include more than 2 values, but they should have the same number of values. If a domain is specified as [x1, x2, ..., xn] and the range is specified as [y1, y2, ..., yn] then values between x1 and x2 are mapped to values between y1 and y2, and values between x2 and x3 are mapped to values between y2 and y3, etc.
Color Scales
The range interval for a continuous scaling function can also specify colors, rather than numbers.
Below we add a third scaling function that we use to compute the colors of the circles.
var cScale = d3.scaleLinear() .domain([0,140000]) .range(["yellow", "red"]);
<svg id="homeless2" width="600" height="100" ></svg> <script> var data = [ {"state": "California","population": 134278 }, {"state": "Florida", "population": 32190 }, {"state": "Washington","population": 21112 }, {"state": "New York","population": 89503 }, {"state": "Texas","population": 23548 } ]; var cScale = d3.scaleLinear() .domain([0,140000]) .range(["yellow", "red"]); var xScale = d3.scaleLinear() .domain([0,4]) .range([50,550]); var rScale = d3.scaleLinear() .domain([0,140000]) .range([1,30]); var u = d3.select("#homeless2") .selectAll("circle") .data(data) .enter() .append("circle") .attr("cx", (d,i) => xScale(i)) .attr("cy", 50) .attr("r", (d) => rScale(d.population)) .attr("fill", (d) => cScale(d.population)); u = d3.select("#homeless2") .selectAll('text') .data(data) .enter() .append('text') .text((d) => d.state) .attr('x', (d,i) => xScale(i)) .attr('y', 95) .attr('text-anchor', 'middle'); </script>
Clamping
By default, each of the continuous scaling functions accept values outside of the domain and uses extrapolation to compute a return value. If we’d like to turn off extrapolation and return only values within the range, we can use the clamp
method.
xScale.clamp(true);
Invert
If the scale function has a numeric domain, the invert
method maps a value within the range to a value within the domain.
Nice
When a domain interval is created using actual data, it is often necessary to extend the domain on both ends so that when data values are scaled, the result is not a boundary of the range. The nice
method does just that; it rounds the endpoints of the domain to nice round values.