首页 文章

D3js中堆积的条形图 - 条形图不在正确的位置

提问于
浏览
0

我正在尝试在D3js中构建一个堆积条形图 . 我有问题设置正确的 yy0 属性并在正确的位置绘制条形图 . 可能我有一个计算错误,但我找不到它 . 这是示例代码的链接FIDDLE场景是:

  • 我首先按"period"对数据进行分组,并在xAxis上显示句点

  • 然后我按"type"分组 - MONTH和ENTRY应该是不同颜色的堆叠条 .

  • 每个周期的每种类型的总和"amount"显示在yAxis上 .

我使用带有2个键的嵌套函数来构造数据 . 当我在实际堆叠条形图中绘制条形时,会出现问题 . 我不确定问题是在我访问数据(键和值)的方式,还是我设置属性“y”和“height”的方式 .

selection.selectAll("rect")
    .data(function (d) { return d.values; })
    .enter().append("rect")
    .attr("width", x.rangeBand())
    .attr("y", function (d) { return y(d.values); })
    .attr("height", function (d) { return y(d.y0) + y(d.values); })
    //.attr("height", function (d) { return y(d.y0) - y(d.values); })
    .style("fill", function (d) { return color(d.key); })

明显的错误是其中一个酒吧隐藏在另一个酒吧后面 . 第二个栏位于xAxis下 .

我是d3js的初学者,我找不到解决方案 . 有人能帮助我吗?

1 回答

  • 0

    我可以看到一些东西:

    • 看起来你的 nest 过于复杂 . 您应该只需要嵌套一个级别 .

    • 当您确实希望最大值为堆栈总数时,您计算的最大值将只是堆栈中单个元素的最大值 .

    • 您正在创建的组元素( g )似乎按"wrong"方式分组 . 您通常希望将每个堆栈的"bit"分组 . 也就是说,您希望每个堆栈的第一个 rect 与其他第一个 rect 在同一组中 . 然后每个堆栈中的第二个将与另一个第二个 rect 分组,依此类推 . 这可能是由于第一点的嵌套错误 .

    • 你实际上需要计算 valueOffset ,这是你在小提琴中得到的,但是被注释掉了 . 该值用于设置构造堆栈时的相对位置 .

    为了帮助,我根据你所写的内容整理了正确的内容 . 看看下面的代码片段 .

    var margin = {top: 20, right: 20, bottom: 30, left: 40},
        width = 400 - margin.left - margin.right,
        height = 400 - margin.top - margin.bottom;
                
    var color = d3.scale.category10();
    
    var data = [
        {
          "period":201409,
          "type":"MONTH",
          "amount":85.0
        },
        {
          "period":201409,
          "type":"ENTRY",
          "amount":111.0
        },
        {
          "period":201410,
          "type":"MONTH",
          "amount":85.0
        },
        {
          "period":201410,
          "type":"ENTRY",
          "amount":55.0
        }   
    ];
        
    var x = d3.scale.ordinal().rangeRoundBands([0, width], .1, 0);
    var y = d3.scale.linear().range([height, 0]);
    
    
    var xAxis = d3.svg.axis()
                    .scale(x)
                    .orient("bottom");
    
    var yAxis = d3.svg.axis()
                    .scale(y)
                    .orient("left").ticks(10);
    
    var svg = d3.select("#chart")
                    .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 + ")");
                    
                data.forEach(function(d) {
                    d["period"] = d["period"];
                    d["amount"] = +d["amount"];
                    d["type"] = d["type"];
                });
    
    var nest = d3.nest()
                    .key(function(d) { return d["type"];});
    
    var dataByType = nest.entries(data);
    //var max = d3.max(dataByGroup, function(d) { return d3.sum(d.values, function(e) { return e.values; }); })
    
                //console.log("dataByGroup", dataByGroup);  
    var stack = d3.layout.stack()
                    .values(function(d) { return d.values; })
                    .x(function(d) { return d.period; })
                    .y(function(d) { return d.amount; })
                    .out(function(d, y0) { 
                      d.valueOffset = y0; 
                    });
                
    //data: key: group element, values: values in each group
    stack(dataByType);
    var yMax = d3.max(dataByType, function(type) { return d3.max(type.values, function(d) { return d.amount + d.valueOffset; }); });
    
    color.domain(dataByType[0].values.map(function(d) { return d.type; }));
    x.domain(dataByType[0].values.map(function(d) { return d.period; }));
    y.domain([0, yMax]);
                
    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);
    
    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
        .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 3)
        .attr("dy", ".71em")
        .style("text-anchor", "end");
    
    var selection = svg.selectAll(".group")
        .data(dataByType)
      .enter().append("g")
        .attr("class", "group");
        //.attr("transform", function(d) { return "translate(0," + y0(y0.domain()[0]) +  ")"; });
    
    selection.selectAll("rect")
        .data(function (d) { return d.values; })
      .enter().append("rect")
        .attr("width", x.rangeBand())
        .attr("x", function(d) { return x(d.period); })
        .attr("y", function (d) { return y(d.amount + d.valueOffset); })
        .attr("height", function (d) { return y(d.valueOffset) - y(d.valueOffset + d.amount); })
        .style("fill", function (d) { return color(d.type); })
        .style("stroke", "grey");
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
    <div id="chart"></div>
    

    关于上述代码段的一些注释(符合我的评论):

    • 一个更简单的巢:
    var nest = d3.nest()
                 .key(function(d) { return d["type"];});
    

    这比前一个简单得多,并且不需要执行汇总功能 . 当您想要汇总数据时通常需要汇总,在这种情况下您不需要汇总,这应该是您的嵌套过于复杂的赠品 .

    • y轴最大值的计算:
    var yMax = d3.max(dataByType, function(type) { return d3.max(type.values, function(d) { return d.amount + d.valueOffset; }); });
    

    这将计算您的轴需要采取的最大值,使一切都很合适 .

    • 如果查看生成的SVG,您将看到我对每个堆栈中 rect 的分组的含义 . 我通常发现它没有"right"方式,但这通常对我来说最好 .

    • stackvalueOffset 的计算:

    d3.layout.stack()
             .values(function(d) { return d.values; })
             .x(function(d) { return d.period; })
             .y(function(d) { return d.amount; })
             .out(function(d, y0) { 
               d.valueOffset = y0; 
             });
    

    计算出的 valueOffset 用于堆栈中的每个 rect 相对于其他 rect 的位置 . 您将看到它使用了几次,计算最大 y 值,每个 recty attr,以及每个 rectheight .

    我没有解释我所做的每一项更改,但希望通过上述内容和代码片段,您将能够解决这些差异并将其应用于您的确切用例 .

相关问题