在 D3js 中遇到 Y 轴问题
Having troubles with Y-axis in D3js
我正在使用 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 && d.value < 4) {
return "rgb(118, 161, 179)"
}
else {
return "rgb(171, 209, 224)";
}
})
})
问题的出现是因为您手动为每个矩形分配了 20 像素的高度,但您给比例的范围是 0 - 240(height
的值)。比例尺会将范围划分为相等的段(带),每个段对应其域中的每个值。当您在域中只有两个值时,它们每个将具有 120 像素的波段(如果有填充则减少)。比例尺无处“知道”您为每个条形分配了仅 20 像素的高度;毕竟,您告诉它在 0 - 240 的范围内均匀分布值。这些相互矛盾的指令是您的条形图未与轴对齐的原因。
使用 d3 刻度时,如果您对两个轴都使用刻度并绘制数据本身 (rects/circles/etc),您会发现它会容易得多:这样它们将始终对齐。
d3 波段尺度提供了一个方便的方法:scale.bandwidth()
,这个 returns 尺度中波段的 length/width/height:最简单的(没有填充)它是大小范围除以域中不同值的数量。我们可以使用这个值来设置条形高度:
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 && 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>
我还注意到您向每个条形的 y 值添加了 10 个像素:这可能是为了更好地手动对齐多个数据条目的条形。通常这会导致问题(除非手动纠正它们):y/x 和 height/width 的 scale(value) 和 scale.bandwidth() 分别生成以轴刻度为中心的条。如果你想要填充(space 在条之间),最简单的设置是使用比例: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 && 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 && 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 高度以使其尺寸更好
我正在使用 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 && d.value < 4) {
return "rgb(118, 161, 179)"
}
else {
return "rgb(171, 209, 224)";
}
})
})
问题的出现是因为您手动为每个矩形分配了 20 像素的高度,但您给比例的范围是 0 - 240(height
的值)。比例尺会将范围划分为相等的段(带),每个段对应其域中的每个值。当您在域中只有两个值时,它们每个将具有 120 像素的波段(如果有填充则减少)。比例尺无处“知道”您为每个条形分配了仅 20 像素的高度;毕竟,您告诉它在 0 - 240 的范围内均匀分布值。这些相互矛盾的指令是您的条形图未与轴对齐的原因。
使用 d3 刻度时,如果您对两个轴都使用刻度并绘制数据本身 (rects/circles/etc),您会发现它会容易得多:这样它们将始终对齐。
d3 波段尺度提供了一个方便的方法:scale.bandwidth()
,这个 returns 尺度中波段的 length/width/height:最简单的(没有填充)它是大小范围除以域中不同值的数量。我们可以使用这个值来设置条形高度:
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 && 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>
我还注意到您向每个条形的 y 值添加了 10 个像素:这可能是为了更好地手动对齐多个数据条目的条形。通常这会导致问题(除非手动纠正它们):y/x 和 height/width 的 scale(value) 和 scale.bandwidth() 分别生成以轴刻度为中心的条。如果你想要填充(space 在条之间),最简单的设置是使用比例: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 && 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 && 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 高度以使其尺寸更好