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

时间:2014-10-15 01:41:40

标签: javascript json d3.js stackedbarseries

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

  1. 我首先将数据分组为“期间”,期间显示在xAxis
  2. 然后我按“类型”进行分组 - MONTH和ENTRY应该是不同颜色的堆叠条。
  3. 每个期间每种类型的总和“金额”显示在yAxis上。
  4. 我使用带有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 :(得分:0)

我可以看到一些事情:

  1. 您似乎过度复杂nest。你应该只需要嵌套一个级别。
  2. 当您真正希望最大值为堆栈的总和时,您计算的最大值将只是堆栈中单个元素的最大值。
  3. 您正在创建的组元素(g)似乎被分组为"错误"办法。您通常希望将相同的" bit"每个堆栈。也就是说,您希望每个堆栈的第一个rect与其他第一个rect在同一个组中。然后,每个堆栈中的第二个将与其他第二个rect组合,依此类推。这可能是由于第一点的嵌套错误。
  4. 你实际上需要计算你在小提琴中得到的valueOffset,但是被注释掉了。该值用于设置构造堆栈时的相对位置。
  5. 为了提供帮助,我根据您所写的内容整理了正确的内容。查看下面的代码段。

    
    
        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>
    &#13;
    &#13;
    &#13;

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

    1. 一个更简单的巢:

      var nest = d3.nest()
                   .key(function(d) { return d["type"];});
      

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

    2. y轴最大值的计算:

      var yMax = d3.max(dataByType, function(type) { return d3.max(type.values, function(d) { return d.amount + d.valueOffset; }); });
      

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

    3. 如果您查看生成的SVG,您将看到我对每个堆栈中rect的分组的含义。我通常发现以这种方式分组更容易。我想那里没有&#34;对&#34;方式,但这通常最适合我。

    4. 计算valueOffset中的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; 
               });
      

      计算出的valueOffset用于&#34;移动&#34;堆栈中的每个rect相对于其他rect的位置。您会看到它使用了几次,计算每个y的最大y值,rect attr以及每个height的{​​{1}} }。

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