Sunday, May 19, 2024
HomeCSSWorking with Color Scales for Information Visualisation in D3

Working with Color Scales for Information Visualisation in D3


Temperature chart with threshold scale applied, showing distinct colour bands
Temperature anomaly information from the World Warming and Local weather Change API, visualised with D3

There are lots of alternative ways of utilizing color informatively in relation to information visualization. However we additionally want to make sure our use of color conveys the correct info, and that it’s used accessibly. On this article we’ll stroll by way of some examples of utilizing color in various kinds of visualizations with the information visualization library D3.js. Some familiarity with D3 will likely be assumed, however there will likely be some useful pointers no matter the way you create your visualizations.

Is color the best choice?

Color will be extraordinarily helpful in conveying that means in our designs. It may also be troublesome to get proper, and within the worst case may have a damaging impression on the person expertise. Some color combos are troublesome to understand by folks with color blindness or low imaginative and prescient. Some colors might need completely different cultural significance in several elements of the world, which may have an effect on how they’re interpreted in a UI. The color pink would possibly signify warning or hazard within the western world, however in East Asian international locations typically represents happiness and success.

Due to this fact, it’s hardly ever a good suggestion to depend on color alone. Earlier than making a chart, we should always take into account what different choices can be found to us for visualizing our information. It’s helpful to bear in mind Gestalt rules: measurement, place, proximity, grouping and distinction could possibly be another methods to distinguish information, in addition to textual content labels when crucial. On the net we’re fortunate as a result of our charts may also be interactive: we are able to progressively disclose extra info to the person, with out overwhelming them.

Color scales

Assuming we’ve established that color is necessary for our visualization, let’s have a look at some color scale choices. We’re going to be utilizing the d3-scale module, and evaluating some completely different use circumstances for its varied scale features.

Overview of scales

The aim of a scale is to map one vary of values to a different. In D3 (and information visualization typically), their function is to map values in a set of knowledge to a visible illustration. We are able to see the precept illustrated in this demo, the place hovering on the bigger space maps the place on the smaller one.

See the Pen
D3 area/vary
by Michelle Barker (@michellebarker)
on CodePen.

The enter (that’s, the information we wish to map) is the area and the output (consisting of the vary of values our mapped information ought to end in) is named the vary.

D3 has a variety of completely different scale features, however we’ll have a look at a handful which can be significantly helpful when working with color in information visualization. To reveal we’re going to visualise information from the World Warming and Local weather Change API. To begin with we’ll fetch information from this endpoint, which supplies us an array of world temperature anomalies from 1893 to the current day. We’ll plot these as a linear gradient in CSS, which can enable us to see the benefits on disadvantages of the various kinds of scales in motion.

To assist visualise the results of making use of completely different color scales, right here is an interactive demo that demonstrates linear, diverging and threshold color scales — the primary ones I’ll be explaining on this article.

See the Pen
D3 colour scale visualizer
by Michelle Barker (@michellebarker)
on CodePen.

Steady scales

Steady scales in D3 allow us to map a steady, quantitative enter area to a steady output vary. Let’s have a look at an instance of a steady color scale utilizing D3’s scaleLinear operate. Right here we’re making a operate to map a website of values from 0 to 100 to a steady color vary from royalblue to pink.

const colorScale = d3
.scaleLinear()
.area([0, 100])
.vary(['royalblue', 'pink'])

If we name our color scale operate, passing in a worth from 0 to 100 we’ll get a color that falls someplace inside that vary. So colorScale(0) will give us royalblue (output as rgb(173, 216, 230)), colorScale(100) will give us pink (rgb(255, 192, 203)), whereas colorScale(50) ends in a lavender color (interpolated from blue and pink).

Screenshot showing the resulting colours on the colour scale

Clamping

Passing in a worth exterior of our anticipated area can lead to some sudden colors. For instance, colorScale(200) ends in rgb(255, 255, 181), a pale yellow color!

Colour swatches showing the (expected) colour values for 0 and 100, and the unexpected yellow colour for 200

An answer is to make use of the clamp technique, which ensures any values exterior of the area will likely be clamped to the given vary, and we gained’t get any sudden outcomes.

const colorScale = d3
.scaleLinear()
.area([0, 100])
.vary(['royalblue', 'pink'])
.clamp(true)

Working with information

When working with information it’s possible we’ll need our area to be decided by the information itself. We are able to use D3’s extent operate to get the very best and lowest values in our dataset as an array. We are able to cross in a easy array, for instance:

d3.extent([0, 20, 50, 100]) // consequence: [0, 100]

Alternatively we are able to use an accessor operate, if we wish to use a selected attribute from our information. Let’s fetch some information from the World Warming and Local weather Change API and use the extent operate to get the area based mostly on land temperature anomaly values from the dataset. We’ll write a brand new operate referred to as draw to do that, seeing as we’ll be utilizing it fairly a bit afterward:

/* Fetch information */
fetch('https://global-warming.org/api/temperature-api')
.then((response) => response.json())
.then((response) => {
const information = downsampleData(response.consequence)
draw(information)
})
.catch((error) => error)

/* Accessor operate to get the land temperature from the datum */
const yAccessor = (d) => parseFloat(d.land)

const draw = (information) => {
/* Retrieve the bottom and highest temperatures from our information */
const area = d3.extent(information, yAccessor) // consequence: [-0.481, 1.018]

const colorScale = d3
.scaleLinear()
.area(area)
.vary(['pink', 'blue'])
.clamp(true)
}

The request response comprises fairly excessive decision information. Within the demo we’re utilizing a operate to downsample the information in order that we’re working with the typical temperature anomaly on a yearly quite than month-to-month foundation. This won’t be crucial to your personal dataset.

Let’s use our linear color scale with our information to render a an SVG with a gradient fill that corresponds to temperature anomaly values over time — the place greater temperatures are represented by colors in direction of the pink finish of the size and decrease temperatures by bluer colors.

/* In our draw operate */
const colorScale = d3
.scaleLinear()
.area(area)
.vary(['pink', 'royalblue'])
.clamp(true)

/* Create SVG */
const svg = chart
.append('svg')
.attr('width', 600)
.attr('top', 400)
.attr('viewBox', '0 0 600 400')

/* Append <defs> ingredient containing <linearGradient>, the place we'll outline the gradient to make use of */
const gradient = svg.append('defs').append('linearGradient').attr('id', 'grad1')

/* Create the gradient stops from our information */
information.forEach((d, index) => {
const colour = colorScale(yAccessor(d))
const step = 100 / information.size

gradient.append('cease').attr('offset', 0).attr('stop-colour', colour)

gradient
.append('cease')
.attr('offset', `${index * step}%`)
.attr('stop-colour', colour)
})

/* Create a rectangle and fill it with the gradient background */
svg
.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', 600)
.attr('top', 400)
.attr('fill', 'url(#grad1)')

We may use a CSS gradient for this explicit visualisation, but when we wish to create a extra advanced chart that features axes, labels, and extra info, utilizing an SVG is way more versatile.

Temperature chart with linear colour scale. Higher temperature anomalies are depicted by pink colours, lower anomalies by blue

Word, after we use a linear scale each our area and vary can embody a better variety of values, nevertheless it usually works finest if each arrays are of the identical size. On this instance colorScale(0) will map to darkish violet, colorScale(100) will map to pink, and so forth.

d3.scaleLinear()
.area([-100, 0, 100])
.vary(['lightblue', 'darkviolet', 'red'])

Diverging scales

We are able to actually use a distinct scale operate for creating diverging color scales: scaleDiverging all the time takes three values for its area, much like our linear scale operate above. However as an alternative of a variety, we are able to specify an interpolator. D3 offers a bunch of color scheme interpolators in its d3-scale-chromatic module. The interpolator d3.interpolateRdYlBu is ideal for our temperature information, because it goes from pink, by way of impartial colors, to darkish blue. It additionally has an excellent diploma of tonal distinction, with the lightness of the colors reducing in direction of both finish of the size. This makes it extra accessible for customers which may discover it troublesome to understand modifications in hue.

We’ll flip the area round on this occasion, in order that greater values will likely be represented by colors in direction of the pink finish of the size and vice versa. We’re setting a center worth of 0, as 0 represents no temperature anomaly, which must be represented by a impartial color.

Let’s apply this scale to our dataset.

/* Change the earlier `colorScale` worth in our draw operate: */
const colorScale = d3
.scaleDiverging(d3.interpolateRdYlBu)
.area([domain[1], 0, area[0]])

See the Pen
D3 color scale linear (diverging)
by Michelle Barker (@michellebarker)
on CodePen.

Interpolation

As we have now seen, D3’s scale features interpolate between two (or extra) colors to supply hues that fall wherever between these in our specified color vary. We are able to select how the colors are interpolated on a linear scale utilizing D3’s varied interpolation strategies. Specifying interpolateCubeHelix as an interpolation technique, for instance, will yield fairly a distinct consequence to interpolateRGB.

const colorScale = d3
.scaleLinear()
.area([0, 100])
.vary(['royalblue', 'pink'])
.interpolate(d3.interpolateCubehelix)

color interpolation is a big matter, past the scope of this text. Try the documentation on color areas within the d3-interpolate module for particulars of the varied interpolation strategies obtainable in D3 — and take a look at taking part in round with them to see the completely different outcomes.

Discrete scales

We’re going to try two of D3’s scale features for working with discrete information — information that consists of particular person or countable values. Utilizing our temperature information, maybe we wish to draw a line graph exhibiting distinct traces for temperature over time throughout the spring, summer season, autumn and winter respectively. Spring, summer season, autumn and winter are discrete values (they’re every distinct), whereas temperature is steady.

Ordinal scale

If we wish to color our traces distinctly for the 4 seasons we may use an ordinal scale. This maps a discrete enter to a discrete output with a 1:1 relationship.

d3.scaleOrdinal()
.area(['spring', 'summer', 'autumn', 'winter'])
.vary(['turquoise', 'pink', 'orangered', 'royalblue'])

See the Pen
D3 ordinal color scale
by Michelle Barker (@michellebarker)
on CodePen.

We might anticipate our information to fall into any considered one of these classes. However, if we had been displaying a bar chart exhibiting the variety of survey respondents categorized by gender, there could possibly be some responses that don’t conform to our three classes: a respondent might need entered a distinct gender of their response, or declined to reply. We are able to use the unknown() technique to outline a color for sudden or undefined values:

d3.scaleOrdinal()
.area(['male', 'female', 'non-binary'])
.vary(['blue', 'cornflowerblue', 'lightblue'])
.unknown('gray')

Threshold scale

A threshold scale is helpful when we have now steady numerical enter that we wish to map to a discrete output. In our temperature information, it could possibly be troublesome to find out small modifications in temperature when utilizing a steady color scale. However utilizing a threshold scale, we are able to present when the temperature crosses a given threshold, and supply visually distinct colors (see the demo). The size’s area ought to include the edge boundaries.

const colorScaleThreshold = d3
.scaleThreshold()
.area([-0.25, 0, 0.25, 0.5, 0.75])
.vary(['royalblue', 'lightblue', 'azure', 'pink', 'hotpink'])

If we use this scale for our temperature information we are able to clearly see the marked improve in temperature anomalies within the early a part of this century. For finer element, we may improve the variety of color thresholds.

Temperature chart with threshold scale applied, showing distinct colour bands

Relatively than manually set our threshold colors, we may compute these from our linear or diverging scales.

/* In our draw operate: */

/* Create the continual color scale to interpolate threshold color values */
const colorScaleDiverging = d3
.scaleDiverging(d3.interpolateRdYlBu)
.area(linearDomain)
.clamp(true)

/* Create the edge scale area - equally spaced values from the information extent */
const totalThresholds = 8
// Calculate the worth between every threshold
const step = (area[1] - area[0]) / totalThresholds

const thresholdScaleDomain = d3.vary(totalThresholds).map((d) => {
return (d + 1) * step + area[0]
})

/* Create the edge scale vary utilizing our earlier color scale operate. It sould have the identical variety of values because the area */
let thresholdScaleRange = thresholdScaleDomain.map((d) => {
return colorScaleDiverging(d)
})

thresholdScaleRange = [
colorScaleLinear(domain[0]), // embody the bottom threshold, so we do not miss out on the primary color
...thresholdScaleRange,
]

/* Use these in our new color scale operate */
const colorScaleThreshold = d3
.scaleThreshold()
.area(thresholdScaleDomain)
.vary(thresholdScaleRange)

Utilizing a threshold scale quite than a linear scale, we are able to extra clearly see the temperature distinction over the long run with getting too granular. Lowering the variety of thresholds creates a starker visible disparity between the start of the final century and up to date years, as we are able to clearly see the proportion of knowledge that falls above given thresholds. Attempt adjusting the variety of thresholds on this demo to see the impression.

See the Pen
D3 color scale threshold (diverging)
by Michelle Barker (@michellebarker)
on CodePen.

Hopefully this text has given you an excellent overview of the place various kinds of scales would possibly come helpful when working with color and information visualisation.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments