#d3.js #charts #data-visualization #bar-chart
Вопрос:
Я работаю над этим проектом с D3js, и сейчас я столкнулся с проблемой. Когда у меня будет больше данных, столбики моей диаграммы будут правильно отображаться в той же строке, что и название, но когда я получу меньше данных из своей базы данных, столбики «потеряют контроль» и будут отображаться выше, чем их название, что приведет к плохому просмотру моей диаграммы.
Вот картинка того, что у меня будет, если я загружу больше данных, сделайте это.
И вот мое второе изображение диаграммы, если я загружу меньше данных.
Я действительно не понимаю, чего мне здесь не хватает, но я считаю, что это что-то с высотой оси Y и положением стержней y. Не могли бы вы, пожалуйста, помочь мне разобраться в этом?
Вот мой код:
var margin = { top: 20, right: 30, bottom: 40, left: 90 },
width = 360 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width margin.left margin.right)
.attr("height", height margin.top margin.bottom)
.append("g")
.attr("transform",
"translate(" margin.left "," margin.top ")");
// Parse the Data
var data2 = d3.json("/Events/BarChart/4").then(function (data) {
console.log(data);
// Add X axis
var x = d3.scaleLinear()
.domain([0, 5])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," height ")")
.call(d3.axisBottom(x))
.selectAll("text")
.attr("transform", "translate(-10,0)rotate(-45)")
.style("text-anchor", "end")
;
// Y axis
var y = d3.scaleBand()
.range([0, height])
.domain(data.map(function (d) { return d.name; }))
svg.append("g")
.call(d3.axisLeft(y))
//Bars
svg.selectAll("myRect")
.data(data)
.enter()
.append("rect")
.attr("x", 4)
.attr("y", function (d) { return y(d.name) 10; })
.attr("width", function (d) { return x(d.value); })
.attr("height", 20)
.attr("fill", function (d) {
if (d.value > 1) {
return "rgb(51, 80, 92)";
}
else if (d.value > 1 amp;amp; d.value < 4) {
return "rgb(118, 161, 179)"
}
else {
return "rgb(171, 209, 224)";
}
})
})
Ответ №1:
Проблема возникает из — за того, что вы вручную назначаете каждому прямоугольнику высоту 20 пикселей, но задаете диапазон масштаба от 0 до 240 (значение height
). Шкала разделит диапазон на равные сегменты (полосы), по одному для каждого значения в своей области. Если у вас есть только два значения в домене, они будут иметь полосы по 120 пикселей каждая (уменьшенные, если есть заполнение). Нигде шкала «не знает», что вы назначили высоту всего 20 пикселей для каждого бара; в конце концов, вы сказали ей равномерно распределять значения в диапазоне от 0 до 240. Эти противоречивые инструкции объясняют, почему ваши столбики не выровнены по вашей оси.
При использовании шкал d3 вам будет намного проще, если вы будете использовать шкалу как для оси, так и для рисования самих данных (прямые/круги/и т.д.): таким образом, они всегда будут выровнены.
Шкала диапазона d3 предлагает удобный метод: scale.bandwidth()
, это возвращает длину/ширину/высоту полосы в шкале: в самом простом случае (без заполнения) это размер диапазона, разделенный на количество различных значений в домене. Мы можем использовать это значение для установки высоты полосы:
var margin = { top: 20, right: 30, bottom: 40, left: 90 },
width = 360 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width margin.left margin.right)
.attr("height", height margin.top margin.bottom)
.append("g")
.attr("transform",
"translate(" margin.left "," margin.top ")");
var data = [
{name: "a", value: 1},
{name: "b", value: 2}
]
// Add X axis
var x = d3.scaleLinear()
.domain([0, 5])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," height ")")
.call(d3.axisBottom(x))
.selectAll("text")
.attr("transform", "translate(-10,0)rotate(-45)")
.style("text-anchor", "end")
;
// Y axis
var y = d3.scaleBand()
.range([0, height])
.domain(data.map(function (d) { return d.name; }))
svg.append("g")
.call(d3.axisLeft(y))
//Bars
svg.selectAll("myRect")
.data(data)
.enter()
.append("rect")
.attr("x", 4)
.attr("y", function (d) { return y(d.name); })
.attr("width", function (d) { return x(d.value); })
.attr("height", y.bandwidth())
.attr("fill", function (d) {
if (d.value > 1) {
return "rgb(51, 80, 92)";
}
else if (d.value > 1 amp;amp; d.value < 4) {
return "rgb(118, 161, 179)"
}
else {
return "rgb(171, 209, 224)";
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="my_dataviz"></div>
Я также заметил, что вы добавляете 10 пикселей к значению y каждого бара: вероятно, это было сделано для того, чтобы вручную лучше выровнять бары с несколькими записями данных. Как правило, это вызовет проблемы (если их не исправить вручную): масштаб(значение) и масштаб.полоса пропускания() для y/x и высоты/ширины соответственно создают полосы, центрированные по оси тиков. Если вам нужно заполнение (пространство между столбцами), проще всего установить это с помощью шкалы: scale.padding(number)
где number
значение от 0 до 1, представляющее часть каждого пустого сегмента:
var margin = { top: 20, right: 30, bottom: 40, left: 90 },
width = 360 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width margin.left margin.right)
.attr("height", height margin.top margin.bottom)
.append("g")
.attr("transform",
"translate(" margin.left "," margin.top ")");
var data = [
{name: "a", value: 1},
{name: "b", value: 2}
]
// Add X axis
var x = d3.scaleLinear()
.domain([0, 5])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," height ")")
.call(d3.axisBottom(x))
.selectAll("text")
.attr("transform", "translate(-10,0)rotate(-45)")
.style("text-anchor", "end")
;
// Y axis
var y = d3.scaleBand()
.range([0, height])
.padding(0.1)
.domain(data.map(function (d) { return d.name; }))
svg.append("g")
.call(d3.axisLeft(y))
//Bars
svg.selectAll("myRect")
.data(data)
.enter()
.append("rect")
.attr("x", 4)
.attr("y", function (d) { return y(d.name); })
.attr("width", function (d) { return x(d.value); })
.attr("height", y.bandwidth())
.attr("fill", function (d) {
if (d.value > 1) {
return "rgb(51, 80, 92)";
}
else if (d.value > 1 amp;amp; d.value < 4) {
return "rgb(118, 161, 179)"
}
else {
return "rgb(171, 209, 224)";
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="my_dataviz"></div>
Но что, если вам не нужны сегменты шириной 120 пикселей? Вы хотите, чтобы ваши полосы всегда были размером 20 пикселей, независимо от того, сколько у вас баров. Ну, мы можем изменить диапазон шкалы, чтобы отразить длину домена:
var margin = { top: 20, right: 30, bottom: 40, left: 90 },
width = 360 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width margin.left margin.right)
.attr("height", height margin.top margin.bottom)
.append("g")
.attr("transform",
"translate(" margin.left "," margin.top ")");
var data = [
{name: "a", value: 1},
{name: "b", value: 2}
]
// Add X axis
var x = d3.scaleLinear()
.domain([0, 5])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," data.length*20 ")")
.call(d3.axisBottom(x))
.selectAll("text")
.attr("transform", "translate(-10,0)rotate(-45)")
.style("text-anchor", "end")
;
// Y axis
var y = d3.scaleBand()
.domain(data.map(function (d) { return d.name; }))
.range([0, data.length*20])
.padding(0.1);
svg.append("g")
.call(d3.axisLeft(y))
//Bars
svg.selectAll("myRect")
.data(data)
.enter()
.append("rect")
.attr("x", 4)
.attr("y", function (d) { return y(d.name); })
.attr("width", function (d) { return x(d.value); })
.attr("height", y.bandwidth())
.attr("fill", function (d) {
if (d.value > 1) {
return "rgb(51, 80, 92)";
}
else if (d.value > 1 amp;amp; d.value < 4) {
return "rgb(118, 161, 179)"
}
else {
return "rgb(171, 209, 224)";
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="my_dataviz"></div>
Я также обновил преобразование для оси x, вы также можете продолжить настройку высоты svg для лучшего размера
Комментарии:
1. Ух ты! Большое вам спасибо за всю эту информацию. Очень признателен. Так приятно просыпаться утром и видеть решение своей проблемы, из-за которого ты почти не спал, ха-ха. Еще раз спасибо, приятель.