在 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 高度以使其尺寸更好