Точечный масштаб: d3.scalePoint()

Масштабирование точек в библиотеке D3.js применяется для равномерного размещения дискретных значений вдоль непрерывной оси. Механизм d3.scalePoint() предназначен для преобразования набора категориальных данных в набор равномерно распределённых координат, где каждая категория получает строго одну позицию без ширины интервала.

Такой тип шкалы используется в ситуациях, когда требуется выстроить элементы по оси X или Y с одинаковыми промежутками: подписи категорий, метки временных интервалов, узлы диаграмм, элементы легенды, дискретные состояния.


Основные характеристики точечной шкалы

d3.scalePoint() относится к семейству порядковых шкал и работает с дискретным доменом:

  • Домен (domain) — набор уникальных категорий
  • Диапазон (range) — числовой интервал координат (обычно пиксели)
  • Выходное значение — точка на оси для каждой категории

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


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

Базовая инициализация:

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

После этого:

scale("A"); // 0
scale("B"); // 100
scale("C"); // 200
scale("D"); // 300

Фактические значения зависят от количества элементов и параметра padding, который изменяет распределение точек внутри диапазона.


Математическая модель распределения

Расположение точек вычисляется через равномерное разбиение диапазона.

Пусть:

  • n — количество элементов в домене
  • [a, b] — диапазон шкалы
  • p — padding (от 0 до 1)

Тогда шаг между точками:

[ step = ]

Начальное смещение:

[ offset = step p]

Положение каждой точки:

[ x_i = a + offset + i step]

где i — индекс элемента в домене.

Такое распределение гарантирует симметричное размещение с возможностью добавления внутренних отступов.


Отличие от scaleBand()

Частая ошибка при выборе шкалы — использование scalePoint() вместо scaleBand().

Основное различие:

  • scalePoint():

    • каждая категория → одна точка
    • ширина элемента = 0
    • используется для линий, узлов, меток
  • scaleBand():

    • каждая категория → полоса (band)
    • есть ширина и внутренние интервалы
    • используется для столбчатых диаграмм

Параметр padding

padding управляет расстоянием между крайними точками и границами диапазона, а также плотностью распределения.

const scale = d3.scalePoint()
  .domain(["Jan", "Feb", "Mar", "Apr"])
  .range([0, 400])
  .padding(0.5);

Поведение:

  • padding = 0 — точки начинают и заканчиваются строго на границах диапазона
  • padding > 0 — добавляется дополнительное пространство слева и справа
  • при увеличении значения точки «сжимаются» ближе к центру диапазона

Пример использования в оси X

const width = 500;

const scaleX = d3.scalePoint()
  .domain(["Mon", "Tue", "Wed", "Thu", "Fri"])
  .range([0, width])
  .padding(0.2);

const axis = d3.axisBottom(scaleX);

Визуально это приводит к равномерному распределению дней недели вдоль оси X.


Использование в SVG графике

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

const data = ["A", "B", "C", "D"];

const scale = d3.scalePoint()
  .domain(data)
  .range([50, 450])
  .padding(0.3);

svg.selectAll("circle")
  .data(data)
  .enter()
  .append("circle")
  .attr("cx", d => scale(d))
  .attr("cy", 100)
  .attr("r", 6);

Здесь каждая категория преобразуется в координату cx, образуя равномерную линию точек.


Поведение при изменении домена

Изменение массива домена полностью перестраивает распределение:

scale.domain(["A", "B", "C"]);

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

  • позиции перерассчитываются автоматически
  • порядок элементов домена напрямую влияет на координаты
  • добавление или удаление значений изменяет шаг между точками

Роль порядка элементов

В отличие от линейных шкал, порядок домена критически важен.

.domain(["C", "A", "B"])

Элементы будут размещены строго в указанной последовательности, независимо от их «логического» порядка.


Поведение вне домена

При передаче значения, отсутствующего в домене:

scale("Z"); // undefined

Результат всегда undefined, так как шкала не выполняет интерполяцию для категорий вне домена.


Клонирование и копирование шкал

Шкала поддерживает создание независимых копий:

const scale2 = scale.copy();

Это позволяет:

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

Автоматическое округление

Шкала может включать округление координат:

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

При включении:

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

Практическое применение

1. Категориальные оси

  • дни недели
  • месяцы
  • группы пользователей
  • этапы процесса

2. Диаграммы узлов

  • графы
  • сетевые структуры
  • деревья

3. Визуализация временных событий

  • события на таймлайне
  • дискретные интервалы

Взаимодействие с другими шкалами D3

scalePoint() часто комбинируется с:

  • d3.scaleLinear() — для числовых значений
  • d3.scaleTime() — для временных осей
  • d3.scaleOrdinal() — для цветовой кодировки категорий

Пример комбинирования:

const x = d3.scalePoint()
  .domain(categories)
  .range([0, width]);

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

Производительность и стабильность

scalePoint() обладает высокой производительностью:

  • вычисления выполняются за O(1) после инициализации
  • домен преобразуется в индексную структуру
  • нет сложной интерполяции

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


Типичные ошибки использования

1. Использование вместо scaleBand()

Приводит к отсутствию ширины у элементов и неправильной визуализации столбцов.

2. Дублирование значений в домене

.domain(["A", "A", "B"])

Результат непредсказуем, так как шкала предполагает уникальность категорий.

3. Неправильный диапазон

Если диапазон слишком мал, точки могут накладываться визуально из-за округления.


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

При пустом домене:

.domain([])

Шкала не может вычислить распределение, и любые обращения возвращают undefined.


Геометрическая интерпретация

Точечная шкала формирует:

  • линейную ось
  • набор равномерных узлов
  • фиксированные координаты без интервалов

Это можно интерпретировать как дискретную сетку, наложенную на непрерывное пространство.


Итоговые особенности модели

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