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.