Sequential & diverging scales

Color scales

Color scales are very common in data visualization. Different hues (red, yellow, blue) that represent different categories or color gradients that represent continuously increasing values.

d3-scale-chromatic provides a number of color scales. This chapter explains how they can be used. These scales are friendly for colorblind users and remain distinguishable in gray-style.

Sequential color scales represent continuous numeric values. A sequential color scale is a color gradient, i.e., an ordered color progression, like colors from light blue to dark blue, that represent numeric values from, say, 0 to 100. D3 provides a few cyclical schemes ("rainbow" schemes), but the sequential schemes are more suitable. Sequential gradients can be in one hue, but gradients with multiple hues (e.g. from purple to red) generally provides higher color contrast, making it easier for the user to read the visualization.

Diverging color scales represent continuous numeric values around a neutral midpoint. For instance, a color gradient form dark blue to white (neutral midpoint) for temperatures below zero degrees Celsius and a color gradient from white to dark red for temperatures above zero degrees Celsius, like in a heat map.

Gradients (sequential or diverging) can also be classed, like in a choropleth map. These classed or quantized gradients are discrete, as opposed to unclassed (continuous) gradients. The numeric values that they represent are still continuous though. Quantile, quantize and threshold scales (explained in a later chapter) can be used to establish classed quantitative color scales.

Categorical color scales (or qualitative color scales) represent discrete values, to distinguish categories. The categories, and consequently the colors, have no mutual order. One color does not have a higher or lower value than the other color. Categorical scales will be explained in a later chapter.

To summarize:

Also see types of input and output.

Sequential scales

d3.scaleSequential is similar to d3.scaleLinear in that it maps a continuous, numeric input domain to a continuous output range by a linear function. Unlike d3.scaleLinear, the output range is interpreted as an interpolator, rather than an array of values.


let colorScale
colorScale = d3.scaleSequential([0, 20], d3.interpolateBlues);
// is equivalent to:
colorScale = d3.scaleSequential(d3.interpolateBlues) 
  .domain([0, 20]); // default doamin is [0,1]
// is equivalent to:
colorScale = d3.scaleSequential() 
  .domain([0, 20])
  .interpolator(d3.interpolateBlues);

However, the range can be specified as an array, but this is equivalent to using d3.scaleLinear with the same range (it uses the default interpolator d3.interpolate()).


let colorScale;
colorScale = d3.scaleSequential()
  .domain([0, 20])
  .range(["purple", "yellow"]);
// is equivalent to:
colorScale = d3.scaleSequential()
  .domain([0, 20])
  .interpolator(d3.interpolateRgb("purple", "yellow")); // 'mixing' two colors	  
// is equivalent to:
colorScale = d3.scaleLinear()
  .domain([0, 20])
  .range(["purple", "yellow"]);

// next does not work:
colorScale = d3.scaleSequential()
  .domain([0, 20])
  .range(d3.interpolateBlues);
// next does not work: 
colorScale = d3.scaleLinear()
  .domain([0, 20])
  .range(d3.interpolateBlues);

A sequential scale’s domain must be numeric. d3.scaleSequential does not have an invert method.

The scale's domain is linearly mapped to the interpolator's domain ([0,1]). For each input value d, the interpolator function is executed for the associated value t (0 ≤ t ≤ 1).


const myScale = d3.scaleSequential()
  .domain([0, 20])
  .interpolator(t => `corresponding value: ${t}`); // interpolator function
 
console.log(myScale(0)) ; // logs: "corresponding value: 0"
console.log(myScale(10)) ; // logs: "corresponding value: 0.5"
console.log(myScale(20)) ; // logs: "corresponding value: 1"

const data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
const colorScale = d3.scaleSequential()
  .domain([0, 20])
  .interpolator(t => `hsl(${t * 360}, 100%, 50%)`); // hsl color code: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl		  
const container = d3.select("#container");
container
  .selectAll("span")
  .data(data)
  .join("span")
  .style("background", d => colorScale(d))
  .style("width", (container.node().offsetWidth - 2)/data.length+"px");

Sequential scales are typically used for color encoding. D3 provides a number of interpolators with preset color schemes. A few examples:


const colorScale = d3.scaleSequential()
  .domain([0, 20])
  .interpolator(d3.interpolateInferno);	

const colorScale = d3.scaleSequential()
  .domain([0, 20])
  .interpolator(d3.interpolateTurbo);	

const colorScale = d3.scaleSequential()
  .domain([0, 20])
  .interpolator(d3.interpolateYlOrRd);	

Non-linear sequential scales

d3.scaleSequential maps the scale's domain to the interpolator's domain by a linear function. D3 also provide a number of sequential scales that maps the scale's domain to the interpolator's domain by a non-linear function.

In the next chapter these transformations (for non-sequential scales) are described in more detail.

Diverging scales

d3.scaleDiverging is similar to d3.scaleSequential, except d3.scaleDiverging's domain includes exactly three values: two extremes, and a central point (default [0, 0.5, 1]). Diverging scales are useful to visualize phenomena that go in two opposite directions, around a neutral midpoint, like positive values for a population that increases and negative values for a population that decrease, or temperatures above and below zero degrees Celsius.

Like sequential scales, diverging scales are typically used for color encoding and work well with diverging color interpolators (they typically join two sequential scales around a midpoint).


const data = [-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12];
const extent = d3.extent(data, d => d);
const colorScale = d3.scaleDiverging(d3.interpolateRdBu)
  .domain([extent[0], 0, extent[1]])		  
const container = d3.select("#container");
container
  .selectAll("span")
  .data(data)
  .join("span")
  .style("background", d => colorScale(d))
  .style("width", (container.node().offsetWidth - 2)/data.length+"px");	

const colorScale = d3.scaleDiverging(t => d3.interpolateRdBu(1 - t))
  .domain([extent[0], 0, extent[1]])		  

In the example above in the second scale, the interpolator is a reversed d3.interpolateRdBu by applying d3.interpolateRdBu to 1 - t instead of t.

Just like sequential scales, D3 also provides non-linear variants of diverging scales: d3.scaleDivergingPow, d3.scaleDivergingSqrt, d3.scaleDivergingLog and d3.scaleDivergingSymlog.