Как мне сделать эти сгруппированные данные ближе друг к другу в D3? - PullRequest
1 голос
/ 26 мая 2020

Я пытаюсь исправить поля для сгруппированных столбцов на моем графике D3. js.

Прямо сейчас у него есть уродливые пробелы, например:

https://i.imgur.com/MGJAUJg.jpg

Но мне нужно, чтобы он выглядел так для каждой группы, с центром на метках оси x (месяц и год):

enter image description here

Я думаю, что эта строка кода является виновником, но я не знаю, как его изменить: .attr("x", function(d) { return x1(d.grpName); })

См. фрагмент кода ниже в виде полной страницы.

const groupData = [
                 { key: "Jan. 2020", values:
                                              [
                                                {grpName:'Team1', grpValue:26},
                                                {grpName:'Team2', grpValue:15},
                                                {grpName:'Team3', grpValue:48}
                                              ]
                 },
                 { key: "Feb.2020", values:
                                              [
                                                {grpName:'Team1', grpValue:14},
                                                {grpName:'Team2', grpValue:23},
                                                {grpName:'Team3', grpValue:5}
                                              ]
                 },
                 { key: "March 2020", values:
                                              [
                                                {grpName:'Team1', grpValue:32},
                                                {grpName:'Team2', grpValue:9},
                                                {grpName:'Team3', grpValue:25}
                                              ]
                 },
                 { key: "April 2020", values:
                                              [
                                                {grpName:'Team1', grpValue:41},
                                                {grpName:'Team2', grpValue:55},
                                                {grpName:'Team3', grpValue:26}
                                              ]
                 }                            
                  ];


    var margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = 1200 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

    
   
    var x0  = d3.scaleBand().rangeRound([0, width], .5);
    var x1  = d3.scaleBand();
    var y   = d3.scaleLinear().rangeRound([height, 0]);

    var xAxis = d3.axisBottom().scale(x0)
                              

    var yAxis = d3.axisLeft().scale(y);

    const color = d3.scaleOrdinal(d3.schemeCategory10);

  var svg = d3.select('#barChart')
    .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 categoriesNames = groupData.map(function(d) { return d.key; });
    var rateNames       = groupData[0].values.map(function(d) { return d.grpName; });

    x0.domain(categoriesNames);
    x1.domain(rateNames).rangeRound([0, x0.bandwidth()]);
    y.domain([0, d3.max(groupData, function(key) { return d3.max(key.values, function(d) { return d.grpValue; }); })]);

    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);


    svg.append("g")
      .attr("class", "y axis")
      .style('opacity','0')
      .call(yAxis)
        .append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", 6)
            .attr("dy", ".71em")
            .style("text-anchor", "end")
            .style('font-weight','bold')
            .text("Value");

    svg.select('.y').transition().duration(500).delay(1300).style('opacity','1');

    var slice = svg.selectAll(".slice")
      .data(groupData)
      .enter().append("g")
      .attr("class", "g")
      .attr("transform",function(d) { return "translate(" + x0(d.key) + ",0)"; });

      slice.selectAll("rect")
      .data(function(d) { return d.values; })
        .enter().append("rect")
            .attr("width", "35")
            .attr("x", function(d) { return x1(d.grpName); })
             .style("fill", function(d) { return color(d.grpName) })
             .attr("y", function(d) { return y(0); })
             .attr("height", function(d) { return height - y(0); })
            .on("mouseover", function(d) {
                d3.select(this).style("fill", d3.rgb(color(d.grpName)).darker(2));
            })
            .on("mouseout", function(d) {
                d3.select(this).style("fill", color(d.grpName));
            });


    slice.selectAll("rect")
      .transition()
      .delay(function (d) {return Math.random()*1000;})
      .duration(1000)
      .attr("y", function(d) { return y(d.grpValue); })
      .attr("height", function(d) { return height - y(d.grpValue); });

      //Legend
  var legend = svg.selectAll(".legend")
      .data(groupData[0].values.map(function(d) { return d.grpName; }).reverse())
  .enter().append("g")
      .attr("class", "legend")
      .attr("transform", function(d,i) { return "translate(0," + i * 20 + ")"; })
      .style("opacity","0");

  legend.append("rect")
      .attr("x", width - 18)
      .attr("width", 18)
      .attr("height", 18)
      .style("fill", function(d) { return color(d); });

  legend.append("text")
      .attr("x", width - 24)
      .attr("y", 9)
      .attr("dy", ".35em")
      .style("text-anchor", "end")
      .text(function(d) {return d; });

  legend.transition().duration(500).delay(function(d,i){ return 1300 + 100 * i; }).style("opacity","1");
  .axis path,
  .axis line {
    fill: none;
    stroke: #000;\
    shape-rendering: crispEdges;
  }
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="barChart"></div>

1 Ответ

1 голос
/ 26 мая 2020

У вас есть две шкалы x, x0 и x1, первая для размещения групп, вторая для размещения столбцов в каждой группе. Однако, хотя вы используете x0.bandwidth() для определения ширины каждой группы, вы используете 35 для определения ширины каждой полосы.

Если x0.bandwidth() меньше или намного больше, чем в 35 раз больше количества столбцов, мы получаем неудобное позиционирование.

Вместо этого вы должны использовать x1.bandwidth(), чтобы установить ширину столбца точно так же, как вы использовали x0.bandwidth для установки ширины группы (диапазон x1).

Если вы используете x1.bandwidth() для установки ширины полосы, вы получаете полосы, которые касаются соседних полос, но теперь вы можете использовать scaleBand.padding() для установки поля между группой и полосой:

 x0.padding(0.1);  // space the groups: bandwidth 90% of original, now with 10% margin
 x1.padding(0.1); // same for spacing the bars

В совокупности:

const groupData = [
                 { key: "Jan. 2020", values:
                                              [
                                                {grpName:'Team1', grpValue:26},
                                                {grpName:'Team2', grpValue:15},
                                                {grpName:'Team3', grpValue:48}
                                              ]
                 },
                 { key: "Feb.2020", values:
                                              [
                                                {grpName:'Team1', grpValue:14},
                                                {grpName:'Team2', grpValue:23},
                                                {grpName:'Team3', grpValue:5}
                                              ]
                 },
                 { key: "March 2020", values:
                                              [
                                                {grpName:'Team1', grpValue:32},
                                                {grpName:'Team2', grpValue:9},
                                                {grpName:'Team3', grpValue:25}
                                              ]
                 },
                 { key: "April 2020", values:
                                              [
                                                {grpName:'Team1', grpValue:41},
                                                {grpName:'Team2', grpValue:55},
                                                {grpName:'Team3', grpValue:26}
                                              ]
                 }                            
                  ];


    var margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = 1200 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

    
   
    var x0  = d3.scaleBand().rangeRound([0, width], .5).padding(0.1);
    var x1  = d3.scaleBand().padding(0.1);
    var y   = d3.scaleLinear().rangeRound([height, 0]);

    var xAxis = d3.axisBottom().scale(x0)
                              

    var yAxis = d3.axisLeft().scale(y);

    const color = d3.scaleOrdinal(d3.schemeCategory10);

  var svg = d3.select('#barChart')
    .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 categoriesNames = groupData.map(function(d) { return d.key; });
    var rateNames       = groupData[0].values.map(function(d) { return d.grpName; });

    x0.domain(categoriesNames);
    x1.domain(rateNames).rangeRound([0, x0.bandwidth()]);
    y.domain([0, d3.max(groupData, function(key) { return d3.max(key.values, function(d) { return d.grpValue; }); })]);

    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);


    svg.append("g")
      .attr("class", "y axis")
      .style('opacity','0')
      .call(yAxis)
        .append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", 6)
            .attr("dy", ".71em")
            .style("text-anchor", "end")
            .style('font-weight','bold')
            .text("Value");

    svg.select('.y').transition().duration(500).delay(1300).style('opacity','1');

    var slice = svg.selectAll(".slice")
      .data(groupData)
      .enter().append("g")
      .attr("class", "g")
      .attr("transform",function(d) { return "translate(" + x0(d.key) + ",0)"; });

      slice.selectAll("rect")
      .data(function(d) { return d.values; })
        .enter().append("rect")
            .attr("width", x1.bandwidth())
            .attr("x", function(d) { return x1(d.grpName); })
             .style("fill", function(d) { return color(d.grpName) })
             .attr("y", function(d) { return y(0); })
             .attr("height", function(d) { return height - y(0); })
            .on("mouseover", function(d) {
                d3.select(this).style("fill", d3.rgb(color(d.grpName)).darker(2));
            })
            .on("mouseout", function(d) {
                d3.select(this).style("fill", color(d.grpName));
            });


    slice.selectAll("rect")
      .transition()
      .delay(function (d) {return Math.random()*1000;})
      .duration(1000)
      .attr("y", function(d) { return y(d.grpValue); })
      .attr("height", function(d) { return height - y(d.grpValue); });

      //Legend
  var legend = svg.selectAll(".legend")
      .data(groupData[0].values.map(function(d) { return d.grpName; }).reverse())
  .enter().append("g")
      .attr("class", "legend")
      .attr("transform", function(d,i) { return "translate(0," + i * 20 + ")"; })
      .style("opacity","0");

  legend.append("rect")
      .attr("x", width - 18)
      .attr("width", 18)
      .attr("height", 18)
      .style("fill", function(d) { return color(d); });

  legend.append("text")
      .attr("x", width - 24)
      .attr("y", 9)
      .attr("dy", ".35em")
      .style("text-anchor", "end")
      .text(function(d) {return d; });

  legend.transition().duration(500).delay(function(d,i){ return 1300 + 100 * i; }).style("opacity","1");
.axis path,
  .axis line {
    fill: none;
    stroke: #000;\
    shape-rendering: crispEdges;
  }
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="barChart"></div>
...