Skip to content

D3.js - Scales API

Scales are a cornerstone of D3.js, providing functions that map an input domain (your data values, e.g., 0 to 100) to an output range (visual values, e.g., 0 to 500 pixels, or a set of colors). This transformation is essential for fitting data onto a visual medium like an SVG canvas. The d3-scale module offers various types of scales.

To use the D3.js Scales API, include the main D3.js library, which contains the d3-scale module and its dependencies like d3-array, d3-interpolate, etc.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D3.js Scales Example</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<svg id="chart" width="500" height="150"></svg>
<script>
// Your D3.js code using scales will go here
</script>
</body>
</html>
  • Domain: The domain defines the extent of your input data values. For quantitative data, it’s often an array of two numbers [min, max]. For ordinal data, it’s an array of distinct category names ["A", "B", "C"].
  • Range: The range defines the extent of the output visual values. For positioning, it might be [0, width] or [height, 0] (for y-axes). For colors, it could be ["red", "blue"] or an array of color codes.

D3 provides several types of scales for different data characteristics and visual mappings:

These map a continuous, quantitative input domain to a continuous output range.

  • d3.scaleLinear(): Constructs a linear scale. The most common scale for quantitative data where the relationship between input and output is linear. scale(value) returns the mapped value.
  • .domain([min, max]): Sets the input domain.
  • .range([start, end]): Sets the output range.
  • .invert(value): Returns the input domain value corresponding to an output range value.
  • .nice(): Extends the domain to round, human-friendly values.
  • d3.scalePow(): A power scale (output = m * input^k + b). Use .exponent(k) to set the power.
  • d3.scaleSqrt(): A square root scale (equivalent to d3.scalePow().exponent(0.5)).
  • d3.scaleLog(): A logarithmic scale. Useful when data has wide variations in magnitude.
  • .base(b): Sets the logarithm base (default 10).
  • d3.scaleTime(): A linear scale for time data (JavaScript Date objects).
  • d3.scaleSequential(interpolator): Maps a continuous domain (usually [0, 1]) to a range defined by an interpolator function (e.g., d3.interpolateViridis for color schemes).

These map a discrete input domain (categories) to a discrete or continuous output range.

  • d3.scaleOrdinal(): Maps a discrete domain to a discrete range (e.g., mapping category names to colors). If the range has fewer values than the domain, it will repeat.
  • .domain(["A", "B", "C"])
  • .range(["red", "green", "blue"]) or d3.schemeCategory10 (an array of 10 categorical colors).
  • d3.scaleBand(): Used for ordinal data where the output range is continuous and numeric, typically for bar charts. It splits the range into uniform bands.
  • .domain(["A", "B", "C"])
  • .range([0, width])
  • .padding(value): Sets padding between bands (0 to 1).
  • scale.bandwidth(): Returns the width of each band.
  • d3.scalePoint(): Similar to scaleBand but for point marks (e.g., ticks on an axis). bandwidth() is always 0.

These map a continuous input domain to a discrete output range (e.g., for choropleth maps).

  • d3.scaleQuantize(): Divides a continuous domain into uniform segments and maps them to discrete values from the range.
  • .domain([0, 100])
  • .range(["low", "medium", "high"])
  • d3.scaleQuantile(): Maps a continuous domain based on sample quantiles to a discrete range. Values are assigned to range buckets based on their rank in the input data.
  • d3.scaleThreshold(): Maps arbitrary continuous domain subsets to discrete range values based on specified thresholds.

Let’s create a simple bar chart using d3.scaleLinear() to map data values to bar widths.

Step 1: Define data and SVG dimensions.

const data = [100, 200, 50, 300, 150];
const svgWidth = 500;
const barHeight = 20;
const barMargin = 1; // Margin between bars
const svgHeight = (barHeight + barMargin) * data.length;

Step 2: Create the linear scale.

const xScale = d3.scaleLinear()
.domain([0, d3.max(data)]) // Input data: 0 to max value in data
.range([0, svgWidth]); // Output: 0 to SVG width for bar lengths

Here, d3.min(data) and d3.max(data) (from d3-array) are convenient for finding domain boundaries. We use 0 as the minimum for the domain, common for bar charts starting from a zero baseline.

Step 3: Set up the SVG canvas.

const svg = d3.select("#chart") // Assuming <svg id="chart"> exists
.attr("width", svgWidth)
.attr("height", svgHeight);

Step 4: Create groups for each bar and apply transformations.

const barGroups = svg.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", (d, i) => `translate(0, ${i * (barHeight + barMargin)})`);

Step 5: Append <rect> elements for bars, using the scale for width.

barGroups.append("rect")
.attr("width", d => xScale(d)) // Use the scale here!
.attr("height", barHeight)
.attr("fill", "steelblue");

Step 6: Append <text> elements for labels, also using the scale for positioning.

barGroups.append("text")
.attr("x", d => xScale(d) + 5) // Position text slightly after the bar end
.attr("y", barHeight / 2)
.attr("dy", ".35em") // Vertical alignment
.text(d => d)
.attr("fill", "black");

Full HTML for the example:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D3.js scaleLinear Example</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
#chart text {
font-size: 10px;
font-family: sans-serif;
}
</style>
</head>
<body>
<svg id="chart"></svg>
<script>
const data = [100, 200, 50, 300, 150];
const svgWidth = 500;
const barHeight = 20;
const barMargin = 1;
const svgHeight = (barHeight + barMargin) * data.length;
const xScale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([0, svgWidth]);
const svg = d3.select("#chart")
.attr("width", svgWidth)
.attr("height", svgHeight);
const barGroups = svg.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", (d, i) => `translate(0, ${i * (barHeight + barMargin)})`);
barGroups.append("rect")
.attr("width", d => xScale(d))
.attr("height", barHeight)
.attr("fill", "steelblue");
barGroups.append("text")
.attr("x", d => xScale(d) + 5)
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(d => d)
.attr("fill", "black");
</script>
</body>
</html>

When opened in a browser, this code will display a horizontal bar chart where each bar’s length is proportional to its data value, scaled to fit within the SVG width. Text labels will show the original data values next to each bar.