d3-scale provides utility functions and constants that support scale operations, axis formatting, and scale behavior configuration.
Intelligent automatic formatting for numeric tick labels on axes and legends.
/**
* Generate an appropriate number format for tick labels
* @param start - Start of the domain range
* @param stop - End of the domain range
* @param count - Approximate number of ticks
* @param specifier - Optional d3-format specifier string
* @returns Function that formats numbers for display
*/
function tickFormat(start: number, stop: number, count: number, specifier?: string): (n: number) => string;The tickFormat function analyzes the numeric range and count to determine the most appropriate formatting, considering:
Usage Examples:
import { tickFormat } from "d3-scale";
// Basic numeric formatting
const formatter1 = tickFormat(0, 100, 10);
console.log(formatter1(0)); // "0"
console.log(formatter1(50)); // "50"
console.log(formatter1(100)); // "100"
// Decimal formatting for small ranges
const formatter2 = tickFormat(0, 1, 10);
console.log(formatter2(0.1)); // "0.1"
console.log(formatter2(0.25)); // "0.25"
console.log(formatter2(0.5)); // "0.5"
// Large number formatting
const formatter3 = tickFormat(0, 1000000, 5);
console.log(formatter3(0)); // "0"
console.log(formatter3(250000)); // "250k" or "250,000"
console.log(formatter3(1000000)); // "1M" or "1,000,000"
// Scientific notation for very large ranges
const formatter4 = tickFormat(0, 1e12, 10);
console.log(formatter4(1e11)); // "1e+11" or formatted appropriately
// Custom format specifier
const percentFormatter = tickFormat(0, 1, 10, "%");
console.log(percentFormatter(0.5)); // "50%"
const currencyFormatter = tickFormat(0, 1000, 10, "$,.0f");
console.log(currencyFormatter(500)); // "$500"Special symbols and constants used for scale behavior configuration.
/**
* Symbol used with ordinal scales to indicate implicit domain expansion
* When set as the unknown value, causes ordinal scales to automatically
* add new domain values as they are encountered
*/
const scaleImplicit: symbol;The scaleImplicit symbol enables ordinal scales to automatically expand their domain when encountering new values, rather than returning the unknown value.
Usage Examples:
import { scaleOrdinal, scaleImplicit } from "d3-scale";
// Ordinal scale with explicit domain
const explicitScale = scaleOrdinal()
.domain(['A', 'B', 'C'])
.range(['red', 'green', 'blue'])
.unknown('gray');
console.log(explicitScale('A')); // 'red'
console.log(explicitScale('D')); // 'gray' (unknown value)
console.log(explicitScale.domain()); // ['A', 'B', 'C'] (unchanged)
// Ordinal scale with implicit domain expansion
const implicitScale = scaleOrdinal()
.range(['red', 'green', 'blue'])
.unknown(scaleImplicit);
console.log(implicitScale('A')); // 'red' (A added to domain)
console.log(implicitScale('B')); // 'green' (B added to domain)
console.log(implicitScale('D')); // 'red' (D added, cycles through range)
console.log(implicitScale.domain()); // ['A', 'B', 'D'] (auto-expanded)
// Practical use case: dynamic category assignment
const dynamicColorScale = scaleOrdinal()
.range(['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd'])
.unknown(scaleImplicit);
// Colors assigned automatically as new categories appear
const data = [
{ category: 'Electronics', value: 100 },
{ category: 'Books', value: 50 },
{ category: 'Clothing', value: 75 },
{ category: 'Electronics', value: 80 }, // Reuses existing color
{ category: 'Home & Garden', value: 60 } // New category, gets next color
];
data.forEach(d => {
const color = dynamicColorScale(d.category);
console.log(`${d.category}: ${color}`);
});
console.log(dynamicColorScale.domain()); // ['Electronics', 'Books', 'Clothing', 'Home & Garden']Most continuous scales provide their own tickFormat method that internally uses the utility function:
import { scaleLinear, tickFormat } from "d3-scale";
const scale = scaleLinear()
.domain([0, 100])
.range([0, 500]);
// Scale's built-in tick formatting
const scaleFormatter = scale.tickFormat(10);
console.log(scaleFormatter(50)); // Formatted using scale's knowledge
// Equivalent manual approach
const manualFormatter = tickFormat(0, 100, 10);
console.log(manualFormatter(50)); // Same result
// Custom specifier through scale
const customScaleFormatter = scale.tickFormat(10, ".1f");
console.log(customScaleFormatter(50)); // "50.0"Time scales use specialized formatting but the principle is similar:
import { scaleTime } from "d3-scale";
const timeScale = scaleTime()
.domain([new Date(2020, 0, 1), new Date(2023, 0, 1)])
.range([0, 400]);
// Time-specific formatting
const timeFormatter = timeScale.tickFormat(5);
console.log(timeFormatter(new Date(2021, 6, 1))); // "Jul 2021" or similar
// Custom time format
const customTimeFormatter = timeScale.tickFormat(10, "%Y-%m");
console.log(customTimeFormatter(new Date(2021, 6, 1))); // "2021-07"Combine tick generation with formatting for complete axis solutions:
import { scaleLinear, tickFormat } from "d3-scale";
function generateAxisLabels(scale, tickCount = 10) {
const ticks = scale.ticks(tickCount);
const format = scale.tickFormat(tickCount);
return ticks.map(tick => ({
value: tick,
position: scale(tick),
label: format(tick)
}));
}
const scale = scaleLinear().domain([0, 1000]).range([0, 400]);
const axisLabels = generateAxisLabels(scale, 5);
axisLabels.forEach(({ value, position, label }) => {
console.log(`Value: ${value}, Position: ${position}, Label: "${label}"`);
});Use tick formatting to handle unknown data ranges:
import { tickFormat, extent } from "d3-scale";
function createSmartFormatter(data, tickCount = 10) {
const [min, max] = extent(data);
return tickFormat(min, max, tickCount);
}
// Adapts formatting to data range automatically
const dataset1 = [0.001, 0.005, 0.012, 0.028, 0.045];
const formatter1 = createSmartFormatter(dataset1);
console.log(formatter1(0.012)); // Appropriate decimal precision
const dataset2 = [1000, 5000, 12000, 28000, 45000];
const formatter2 = createSmartFormatter(dataset2);
console.log(formatter2(12000)); // Appropriate thousands formattingUtilities for inspecting scale behavior:
import { scaleOrdinal, scaleImplicit } from "d3-scale";
function inspectOrdinalScale(scale) {
return {
domain: scale.domain(),
range: scale.range(),
isImplicit: scale.unknown() === scaleImplicit,
mappings: scale.domain().map(d => ({ input: d, output: scale(d) }))
};
}
const scale = scaleOrdinal()
.range(['red', 'green', 'blue'])
.unknown(scaleImplicit);
// Test with some values
scale('A');
scale('B');
scale('C');
scale('D');
console.log(inspectOrdinalScale(scale));
// Shows complete scale state including auto-expanded domaintype TickFormatter = (n: number) => string;