Полосовой масштаб: d3.scaleBand()

Полосовая шкала в D3.js предназначена для распределения дискретных категорий по непрерывному диапазону координат. Основная область применения — построение столбчатых диаграмм, где каждая категория получает отдельную «полосу» фиксированной ширины внутри общего диапазона.

Ключевая особенность заключается в том, что шкала работает не с числовыми интервалами, а с множеством категориальных значений, автоматически рассчитывая позиции и размеры полос.


Базовая концепция работы

Полосовая шкала преобразует набор категорий в равномерно распределённые интервалы на числовой оси.

Формально отображение выглядит так:

  • вход: набор категорий (domain)
  • выход: числовые позиции начала полос (range)
  • дополнительно: ширина каждой полосы (bandwidth)

Каждой категории соответствует свой индексный слот в диапазоне.


Синтаксис создания шкалы

const scale = d3.scaleBand()

После создания шкала настраивается через цепочку методов:

const scale = d3.scaleBand()
    .domain(["A", "B", "C"])
    .range([0, 300]);

Domain: набор категорий

Domain задаёт список значений, которые будут отображаться на оси.

.domain(["январь", "февраль", "март", "апрель"])

Особенности:

  • порядок элементов сохраняется
  • дубликаты не допускаются
  • тип значений произвольный: строки, числа, идентификаторы

В отличие от линейных шкал, порядок domain критически влияет на результат отображения.


Range: область отображения

Range задаёт числовой интервал, в который будут распределены категории.

.range([0, 400])

При использовании в SVG:

  • 0 — начало координатной оси
  • 400 — конечная точка оси

Шкала автоматически вычисляет размер каждой полосы на основе количества элементов и доступного диапазона.


Bandwidth: ширина полосы

Метод bandwidth() возвращает вычисленную ширину одной категории.

const width = scale.bandwidth();

Эта величина используется при построении прямоугольников:

  • ширина столбца = bandwidth
  • высота определяется данными

Пример применения:

.attr("width", scale.bandwidth())

Step: шаг между полосами

Метод step() возвращает расстояние между началом соседних полос.

const step = scale.step();

Связь:

  • step = bandwidth + внутренний отступ (paddingInner)

Padding: управление отступами

Полосовая шкала поддерживает два типа отступов:

paddingInner

Определяет расстояние между полосами внутри диапазона.

.scaleBand().paddingInner(0.1)
  • значение 0 → полосы вплотную
  • значение ближе к 1 → сильное разрежение

paddingOuter

Определяет отступы по краям диапазона.

.scaleBand().paddingOuter(0.2)

align: выравнивание распределения

Метод align() управляет положением полос внутри диапазона.

.scaleBand().align(0.5)

Значения:

  • 0 → прижатие к началу диапазона
  • 0.5 → центрирование
  • 1 → прижатие к концу

round: округление координат

Метод round(true) включает округление значений до целых чисел.

.scaleBand().round(true)

Применение:

  • уменьшение визуальных артефактов
  • повышение чёткости в SVG при пиксельной сетке

Поведение шкалы

После настройки шкала выполняет два основных преобразования:

1. Категория → координата

scale("A") // → 0
scale("B") // → 80
scale("C") // → 160

Результат — позиция начала полосы.

2. Категория → ширина

scale.bandwidth() // → 70

Пример построения столбчатой диаграммы

const data = [
  { name: "A", value: 30 },
  { name: "B", value: 80 },
  { name: "C", value: 45 },
  { name: "D", value: 60 }
];

const x = d3.scaleBand()
    .domain(data.map(d => d.name))
    .range([0, 400])
    .paddingInner(0.2);

const y = d3.scaleLinear()
    .domain([0, 100])
    .range([200, 0]);

const svg = d3.select("svg");

svg.selectAll("rect")
    .data(data)
    .enter()
    .append("rect")
    .attr("x", d => x(d.name))
    .attr("y", d => y(d.value))
    .attr("width", x.bandwidth())
    .attr("height", d => 200 - y(d.value));

Особенности привязки данных

Полосовая шкала особенно эффективна при использовании с .data() в D3:

  • каждая категория соответствует одному DOM-элементу
  • порядок элементов строго соответствует domain
  • масштаб гарантирует равномерное распределение

Сравнение с scalePoint

Полосовая шкала отличается от точечной:

Характеристика scaleBand scalePoint
Ширина области есть нет
Поддержка баров да нет
Возвращает bandwidth да нет
Использование столбцы, бары оси, метки

Внутренняя логика распределения

При задании domain и range происходит расчёт:

  1. Вычисляется количество элементов N
  2. Учитываются paddingInner и paddingOuter
  3. Определяется шаг (step)
  4. Вычисляется bandwidth:
bandwidth = (rangeEnd - rangeStart) / (N + padding adjustment)

Каждой категории назначается индекс i:

position = start + i * step

Использование в горизонтальных и вертикальных диаграммах

Вертикальная ориентация

  • x = scaleBand
  • y = scaleLinear
.attr("x", d => x(d.name))
.attr("width", x.bandwidth())

Горизонтальная ориентация

  • y = scaleBand
  • x = scaleLinear
.attr("y", d => y(d.name))
.attr("height", y.bandwidth())

Детерминированность и стабильность

scaleBand сохраняет:

  • стабильный порядок категорий
  • неизменяемые позиции при одинаковом domain
  • предсказуемое распределение даже при изменении range

При добавлении новых категорий все позиции пересчитываются.


Работа с динамическими данными

При обновлении данных:

  • domain пересобирается
  • диапазон перераспределяется
  • bandwidth изменяется автоматически
x.domain(newData.map(d => d.name));

Ошибки при использовании

Типовые проблемы:

1. Отсутствие domain

Шкала возвращает undefined:

d3.scaleBand().range([0, 100])("A") // undefined

2. Несоответствие данных domain

Категории вне domain не отображаются корректно.

3. Игнорирование bandwidth

Использование фиксированной ширины приводит к перекрытиям.


Поведение при малом количестве элементов

При 1–2 элементах:

  • полосы могут занимать почти весь диапазон
  • paddingOuter становится заметным фактором
  • align влияет на визуальное положение

Практическое значение

scaleBand является базовым инструментом построения:

  • столбчатых диаграмм
  • группированных баров
  • сравнительных графиков категорий
  • тепловых таблиц с дискретными осями

Её использование обеспечивает автоматическое управление геометрией элементов без ручных вычислений координат.