D3.js
Box plot (ящик с усами) представляет собой компактное отображение распределения данных через квартильную структуру. Основная цель — визуализация медианы, разброса и потенциальных выбросов без построения полной гистограммы.
Построение опирается на пять ключевых статистик:
Дополнительно используется межквартильный размах:
IQR = Q3 − Q1
На основе IQR определяются границы выбросов:
Все значения за пределами этих границ отображаются как отдельные точки.
Визуальная структура box plot включает:
Реализация начинается с расчёта статистических характеристик массива данных.
const data = [12, 15, 13, 20, 22, 18, 19, 24, 30, 28];
data.sort(d3.ascending);
const q1 = d3.quantile(data, 0.25);
const median = d3.quantile(data, 0.5);
const q3 = d3.quantile(data, 0.75);
const iqr = q3 - q1;
const min = d3.min(data);
const max = d3.max(data);
const lowerBound = q1 - 1.5 * iqr;
const upperBound = q3 + 1.5 * iqr;
const outliers = data.filter(d => d < lowerBound || d > upperBound);
Масштабирование выполняется через линейную шкалу:
const y = d3.scaleLinear()
.domain([min, max])
.range([400, 0]);
Геометрия box plot формируется из SVG-примитивов:
const svg = d3.select("svg");
svg.append("rect")
.attr("x", 100)
.attr("y", y(q3))
.attr("width", 50)
.attr("height", y(q1) - y(q3))
.attr("fill", "#cce5ff")
.attr("stroke", "#333");
svg.append("line")
.attr("x1", 100)
.attr("x2", 150)
.attr("y1", y(median))
.attr("y2", y(median))
.attr("stroke", "black");
Усы строятся как вертикальные линии:
svg.append("line")
.attr("x1", 125)
.attr("x2", 125)
.attr("y1", y(lowerBound))
.attr("y2", y(q1))
.attr("stroke", "black");
svg.append("line")
.attr("x1", 125)
.attr("x2", 125)
.attr("y1", y(q3))
.attr("y2", y(upperBound))
.attr("stroke", "black");
Выбросы отображаются точками:
svg.selectAll("circle")
.data(outliers)
.enter()
.append("circle")
.attr("cx", 125)
.attr("cy", d => y(d))
.attr("r", 3)
.attr("fill", "red");
Violin plot расширяет box plot, заменяя дискретные квартильные характеристики непрерывной оценкой плотности распределения. Основой служит kernel density estimation (KDE).
Функция плотности описывает вероятность значений в каждой точке диапазона:
Форма симметрична относительно центральной оси.
Для построения violin plot используется
d3.contourDensity или ручная реализация KDE.
const kde = kernelDensityEstimator(kernelEpanechnikov(7), y.ticks(40));
const density = kde(data);
Ядровая функция:
function kernelEpanechnikov(k) {
return function(v) {
return Math.abs(v /= k) <= 1 ? 0.75 * (1 - v * v) / k : 0;
};
}
Оценка плотности:
function kernelDensityEstimator(kernel, X) {
return function(V) {
return X.map(x => [
x,
d3.mean(V, v => kernel(x - v))
]);
};
}
Основная геометрия строится как зеркальная area-диаграмма.
const x = d3.scaleLinear()
.domain([0, d3.max(density, d => d[1])])
.range([0, 50]);
const area = d3.area()
.x0(d => -x(d[1]))
.x1(d => x(d[1]))
.y(d => y(d[0]))
.curve(d3.curveCatmullRom);
Отрисовка:
svg.append("path")
.datum(density)
.attr("d", area)
.attr("fill", "#a6cee3")
.attr("stroke", "#333");
Ось симметрии:
svg.append("line")
.attr("x1", 0)
.attr("x2", 0)
.attr("y1", y(d3.min(data)))
.attr("y2", y(d3.max(data)))
.attr("stroke", "black");
Box plot опирается на дискретное статистическое резюмирование:
Violin plot опирается на непрерывную плотность:
В терминах D3.js различие выражается в уровне абстракции:
quantile, min,
maxkernelDensityEstimator и
d3.areaКомбинированный подход часто реализуется наложением элементов:
// violin shape
svg.append("path")
.datum(density)
.attr("d", area)
.attr("fill", "#e0f3f8");
// box plot поверх
svg.append("rect")
.attr("x", -10)
.attr("y", y(q3))
.attr("width", 20)
.attr("height", y(q1) - y(q3))
.attr("fill", "#fc8d62");
Медиана накладывается поверх плотности:
svg.append("line")
.attr("x1", -10)
.attr("x2", 10)
.attr("y1", y(median))
.attr("y2", y(median))
.attr("stroke", "black");
Корректная визуализация требует согласованного масштабирования:
Типичная ошибка заключается в несогласованности шкал y и x, что приводит к искажению формы распределения.
В интерактивных версиях применяются:
transition) при изменении выборкиПример обновления:
svg.selectAll("path")
.data([density])
.join("path")
.transition()
.duration(600)
.attr("d", area);
Box plot формализует распределение через устойчивые статистики, минимизируя влияние крайних значений. Violin plot добавляет оценку формы распределения, позволяя различать:
В D3.js эти различия выражаются исключительно через трансформации данных, без изменения базовой SVG-структуры, что подчёркивает декларативный характер библиотеки.