Столбчатая диаграмма: вертикальная и горизонтальная

Столбчатая диаграмма в D3.js строится на основе сопоставления дискретных категорий с количественными значениями и визуального кодирования этих значений через длину или высоту прямоугольных элементов. Базовая идея заключается в том, что каждая категория отображается отдельной полосой, а значение определяет размер этой полосы вдоль выбранной оси.


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

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

Категориальная переменная name используется для оси X (или Y в горизонтальном варианте), а value определяет длину столбца.


Создание SVG-контейнера

Визуализация строится внутри SVG-элемента, который задаёт область рисования.

const width = 600;
const height = 400;

const svg = d3.select("body")
  .append("svg")
  .attr("width", width)
  .attr("height", height);

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


Линейные и категориальные шкалы

Для преобразования данных в координаты используются шкалы. В столбчатых диаграммах ключевую роль играет комбинация категориальной и линейной шкалы.

const xScale = d3.scaleBand()
  .domain(data.map(d => d.name))
  .range([0, width])
  .padding(0.1);

const yScale = d3.scaleLinear()
  .domain([0, d3.max(data, d => d.value)])
  .nice()
  .range([height, 0]);

Ключевые моменты:

  • scaleBand распределяет категории равномерно по оси.
  • padding управляет расстоянием между столбцами.
  • scaleLinear отвечает за количественное масштабирование значений.

Вертикальная столбчатая диаграмма

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

svg.selectAll(".bar")
  .data(data)
  .enter()
  .append("rect")
  .attr("class", "bar")
  .attr("x", d => xScale(d.name))
  .attr("y", d => yScale(d.value))
  .attr("width", xScale.bandwidth())
  .attr("height", d => height - yScale(d.value))
  .attr("fill", "steelblue");

Геометрическая логика

Каждый прямоугольник определяется четырьмя параметрами:

  • x — положение по горизонтали через категориальную шкалу
  • y — верхняя точка столбца
  • width — ширина полосы
  • height — разница между базовой линией и значением

Ось Y инвертирована, поскольку SVG-координаты начинаются сверху.


Добавление осей

Оси повышают читаемость визуализации.

const xAxis = d3.axisBottom(xScale);
const yAxis = d3.axisLeft(yScale);

svg.append("g")
  .attr("transform", `translate(0, ${height})`)
  .call(xAxis);

svg.append("g")
  .call(yAxis);

Ось X размещается внизу, а ось Y — слева.


Горизонтальная столбчатая диаграмма

Горизонтальная ориентация меняет роли осей: значения откладываются по горизонтали, а категории — по вертикали.

Шкалы

const yScale = d3.scaleBand()
  .domain(data.map(d => d.name))
  .range([0, height])
  .padding(0.1);

const xScale = d3.scaleLinear()
  .domain([0, d3.max(data, d => d.value)])
  .nice()
  .range([0, width]);

Построение прямоугольников

svg.selectAll(".bar")
  .data(data)
  .enter()
  .append("rect")
  .attr("class", "bar")
  .attr("y", d => yScale(d.name))
  .attr("x", 0)
  .attr("height", yScale.bandwidth())
  .attr("width", d => xScale(d.value))
  .attr("fill", "tomato");

Логика преобразования координат

В горизонтальной диаграмме:

  • длина столбца соответствует значению
  • вертикальное положение фиксирует категорию
  • базовая линия располагается слева

Сравнение вертикальной и горизонтальной ориентации

Вертикальная диаграмма:

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

Горизонтальная диаграмма:

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

Работа с отступами и сеткой

Добавление отступов между столбцами управляется через paddingInner и paddingOuter:

d3.scaleBand()
  .paddingInner(0.2)
  .paddingOuter(0.1);

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

const yGrid = d3.axisLeft(yScale)
  .tickSize(-width)
  .tickFormat("");

svg.append("g")
  .call(yGrid);

Цветовое кодирование

Цвет может зависеть от значения или категории:

.attr("fill", d => d.value > 50 ? "green" : "gray");

Или через шкалу:

const colorScale = d3.scaleSequential()
  .domain([0, d3.max(data, d => d.value)])
  .interpolator(d3.interpolateBlues);

Анимация изменения данных

Плавные переходы усиливают восприятие динамики:

svg.selectAll(".bar")
  .data(data)
  .transition()
  .duration(800)
  .attr("height", d => height - yScale(d.value))
  .attr("y", d => yScale(d.value));

Для горизонтальной версии аналогично изменяется width.


Обновление данных (Enter–Update–Exit)

Механизм связывания данных с DOM-элементами:

const bars = svg.selectAll(".bar")
  .data(data);

bars.enter()
  .append("rect")
  .merge(bars)
  .transition()
  .attr("width", d => xScale(d.value));

bars.exit().remove();

Масштабирование и адаптивность

Для динамической подстройки под контейнер используется вычисление размеров:

const width = parseInt(d3.select("#chart").style("width"));

И пересчёт шкал при изменении размеров контейнера позволяет сохранять пропорции.


Обработка подписей

Подписи значений добавляются через текстовые элементы:

svg.selectAll(".label")
  .data(data)
  .enter()
  .append("text")
  .attr("x", d => xScale(d.name) + xScale.bandwidth() / 2)
  .attr("y", d => yScale(d.value) - 5)
  .attr("text-anchor", "middle")
  .text(d => d.value);

В горизонтальной диаграмме подписи обычно размещаются внутри или справа от столбца.


Частые ошибки при построении

  • неправильная инверсия оси Y при вертикальном графике
  • отсутствие range у шкал
  • несоответствие домена данных и категорий
  • игнорирование padding у scaleBand
  • фиксированные размеры SVG без адаптации

Расширенные техники

Сортировка данных

data.sort((a, b) => b.value - a.value);

Позволяет упорядочить столбцы по убыванию.

Фильтрация

data.filter(d => d.value > 40);

Используется для динамического уменьшения количества отображаемых элементов.

Группировка

Для составных диаграмм применяется вложенная структура данных и несколько шкал.


Поведение осей при масштабировании

Оси автоматически пересчитываются при изменении шкал:

svg.select(".x-axis")
  .transition()
  .call(xAxis);

Это критично при анимации или интерактивной фильтрации данных.


Оптимизация производительности

При большом количестве элементов применяются:

  • минимизация перерисовки через join вместо enter/update/exit
  • использование виртуализации данных
  • сокращение числа DOM-операций
  • кэширование шкал
svg.selectAll(".bar")
  .data(data)
  .join("rect")

Итоговая архитектурная модель

Столбчатая диаграмма в D3 строится как система преобразований:

данные → шкалы → координаты → SVG-элементы → оси → интеракции

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