grouped category bar chart with different groups in d3?

前端 未结 1 1696
心在旅途
心在旅途 2020-12-10 23:39

My requirement is to draw a category-grouped bar chart in which each category has a different number of groups, using pure d3. I have no idea how to take domain and range to

相关标签:
1条回答
  • 2020-12-11 00:05

    The issue with the plunker of the answer that you mention is that it will just work for values that have the same children. In order to handle the dynamic children values I took this approach

    Lets create the color mapping for our groups:

    var color = {
      Mechanical: '#4A7B9D',
      Electrical: '#54577C',
      Hydraulic: '#ED6A5A'
    };
    

    We also need a structure with nested values that will be the inner groups:

    // Simulated data structure
    var data = [{
      key: 'Mechanical',
      values: [{
        key: 'Gear',
        value: 11
      }, {
        key: 'Bearing',
        value: 8
      }, {
        key: 'Motor',
        value: 3
      }]
    }];
    

    I created a barPadding value which will dictate the separation between bars:

    var barPadding = 120;
    

    We are going to need a dummy scale to get the rangeBand of the bars, lets do that:

    // dummy array
    var rangeBands = [];
    // cummulative value to position our bars
    var cummulative = 0;
    data.forEach(function(val, i) {
      val.cummulative = cummulative;
      cummulative += val.values.length;
      val.values.forEach(function(values) {
        rangeBands.push(i);
      })
    });
    // set scale to cover whole svg 
    var x_category = d3.scale.linear()
      .range([0, width]);
    
    // create dummy scale to get rangeBands (width/childrenValues)
    var x_defect = d3.scale.ordinal().domain(rangeBands)
    .rangeRoundBands([0, width], .1);
    var x_category_domain = x_defect.rangeBand() * rangeBands.length;
    x_category.domain([0, x_category_domain]);
    

    Then lets add all our category groups g elements:

    var category_g = svg.selectAll(".category")
      .data(data)
      .enter().append("g")
      .attr("class", function(d) {
        return 'category category-' + d.key;
      })
      .attr("transform", function(d) { // offset by inner group size
        var x_group = x_category((d.cummulative * x_defect.rangeBand()));
        return "translate(" + x_group + ",0)";
      })
      .attr("fill", function(d) { // make child elements of group "inherit" this fill
        return color[d.key];
      });
    

    Adding our inner groups g elements:

    var defect_g = category_g.selectAll(".defect")
      .data(function(d) {
        return d.values;
      })
      .enter().append("g")
      .attr("class", function(d) {
        return 'defect defect-' + d.key;
      })
      .attr("transform", function(d, i) { // offset by index
        return "translate(" + x_category((i * x_defect.rangeBand())) + ",0)";
      });
    

    Having our g elements lets add the labels:

    var category_label = category_g.selectAll(".category-label")
      .data(function(d) {
        return [d];
      })
      .enter().append("text")
      .attr("class", function(d) {
        console.log(d)
        return 'category-label category-label-' + d.key;
      })
      .attr("transform", function(d) {
        var x_label = x_category((d.values.length * x_defect.rangeBand() + barPadding) / 2);
        var y_label = height + 30;
        return "translate(" + x_label + "," + y_label + ")";
      })
      .text(function(d) {
        return d.key;
      })
      .attr('text-anchor', 'middle');
    
    var defect_label = defect_g.selectAll(".defect-label")
      .data(function(d) {
        return [d];
      })
      .enter().append("text")
      .attr("class", function(d) {
        console.log(d)
        return 'defect-label defect-label-' + d.key;
      })
      .attr("transform", function(d) {
        var x_label = x_category((x_defect.rangeBand() + barPadding) / 2);
        var y_label = height + 10;
        return "translate(" + x_label + "," + y_label + ")";
      })
      .text(function(d) {
        return d.key;
      })
      .attr('text-anchor', 'middle');
    

    And finally our rects:

    var rects = defect_g.selectAll('.rect')
      .data(function(d) {
        return [d];
      })
      .enter().append("rect")
      .attr("class", "rect")
      .attr("width", x_category(x_defect.rangeBand() - barPadding))
      .attr("x", function(d) {
        return x_category(barPadding);
      })
      .attr("y", function(d) {
        return y(d.value);
      })
      .attr("height", function(d) {
        return height - y(d.value);
      });
    

    Here's the above code in plnkr: https://plnkr.co/edit/L0eQwtEMQ413CpoS5nvo?p=preview

    0 讨论(0)
提交回复
热议问题