Нередко гистограммы отображают такие данные, которые имеют только одну числовую компоненту. В качестве второй компоненты могут выступать различные строки, например, названия. Допустим, нам надо вывести с помощью гистограмм набранные очки футбольных команд и их названия. Если по одной оси можно отобразить числовые значения - очки команд, то возникает вопрос, как поступить с названиями команд. Реализуем эту задачу на примере:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="http://d3js.org/d3.v3.min.js"> </script> <style> .label {} .axis path, .axis line { fill: none; stroke: #333; } .axis .grid-line { stroke: #000; stroke-opacity: 0.2; } .axis text { font: 9px Verdana; text-anchor:end; } </style> <body> <script type="text/javascript"> var height = 500, width = 500, margin=30, data=[ {name: "Ливерпуль", score: 54}, {name: "Ман. Юнайтед", score: 62}, {name: "Арсенал", score: 66}, {name: "Челси", score: 70}, {name: "Ман. Сити", score: 61}, ]; // функция для получения цветов var color = d3.scale.category10(); // длина оси X= ширина контейнера svg - отступ слева и справа var xAxisLength = width - 2 * margin; // длина оси Y = высота контейнера svg - отступ сверху и снизу var yAxisLength = height - 2 * margin; // функция интерполяции значений на ось X var xScale = d3.scale.ordinal() .rangeRoundBands([0, xAxisLength + margin], .1) .domain(data.map(function(d) { return d.name; })); // функция интерполяции значений на ось Y var yScale = d3.scale.linear() .domain([ d3.min(data, function(d) { return d.score - 10; }), d3.max(data, function(d) { return d.score + 10; }) ]).range([yAxisLength, 0]); var svg = d3.select("body").append("svg") .attr("class", "axis") .attr("width", width) .attr("height", height); // создаем ось X var xAxis = d3.svg.axis() .scale(xScale) .orient("bottom"); // создаем ось Y var yAxis = d3.svg.axis() .scale(yScale) .orient("left"); // отрисовка оси Х svg.append("g") .attr("class", "x-axis") .attr("transform", "translate(" + margin + "," + (height - margin) + ")") .call(xAxis); // отрисовка оси Y svg.append("g") .attr("class", "y-axis") .attr("transform", "translate(" + margin + "," + margin + ")") .call(yAxis); // рисуем горизонтальные линии d3.selectAll("g.y-axis g.tick") .append("line") .classed("grid-line", true) .attr("x1", 0) .attr("y1", 0) .attr("x2", xAxisLength) .attr("y2", 0); // создаем элемент g с набором столбиков svg.append("g") .attr("transform", // сдвиг оси вправо "translate(" + margin + ", 0)") .selectAll(".bar") .data(data) .enter().append("rect") .attr("class", "bar") .attr("x", function(d) { return xScale(d.name); }) .attr("width", xScale.rangeBand()) .attr("y", function(d) { return yScale(d.score); }) .attr("height", function(d) { return height - yScale(d.score) - 30; }) .attr("fill", function(d) { return color(d.name); }); </script> </body> </html>
Так как по горизонтали располагаются не какие-то количественные показатели, а названия команд, выполняющие роль подписей к столбикам, то надо применить функцию d3.scale.ordinal():
var xScale = d3.scale.ordinal() .rangeRoundBands([0, xAxisLength + margin], .1) .domain(data.map(function(d) { return d.name; }));
Функция rangeRoundBands()
принимает отрезок, на котором будут располагаться названия команд ([0, xAxisLength + margin]
),
а также коэффициент масштабирования столбиков - 0.1
.
А функция domain()
получает данные, с которыми будет сопоставляться вышеопределенный отрезок [0, xAxisLength + margin].
Для задания цвета каждого столбика здесь определена функция var color = d3.scale.category10();
, которая возвращает один из 10 цветов по порядку.
Как и при линейном графике, здесь также создаются координатные оси, но без вертикальных линий сетки, так как значения по оси Х нам не нужны. И ключевой момент заключается в создании самих столбиков гистограммы:
svg.append("g") .attr("transform", // сдвиг оси вниз и вправо "translate(" + margin + ", 0)") .selectAll(".bar") .data(data) .enter().append("rect") .attr("class", "bar") .attr("x", function(d) { return xScale(d.name); }) .attr("width", xScale.rangeBand()) .attr("y", function(d) { return yScale(d.score) + margin; }) .attr("height", function(d) { return yAxisLength - yScale(d.score); }) .attr("fill", function(d) { return color(d.name); });