【问题标题】:d3 v4 Stacked to Grouped Bar Chart from CSVd3 v4 从 CSV 堆叠到分组条形图
【发布时间】:2019-01-01 13:00:24
【问题描述】:

参考Mike Bostick's Stacked to Grouped Bar Chart example,我正在修改它以使用 CSV 文件。我已经为此工作了几个星期,并查看了 Stack Overflow 和其他地方的无数示例,我被难住了。

堆积条形图有效。

堆积条形图:

当我转换到分组条形图时,我只会在引用堆叠或分组的键或系列时遇到问题。现在所有的矩形都显示在彼此的顶部,而不是彼此相邻。

分组条形图:

在函数transitionStep2()中,我想乘以对应系列或键的数字。我目前乘以这个函数中的数字 1 作为占位符 .attr("x", function(d) { return x(d.data.Year) + x.bandwidth() / 7 * 1; })

<!DOCTYPE html>
<script src="https://d3js.org/d3.v4.min.js"></script>
<html><body>

<form>
  <label><input type="radio" name="mode" style="margin-left: 10" value="step1" checked>1</label>
  <label><input type="radio" name="mode" style="margin-left: 20" value="step2">2</label>
</form>

<svg id = "bar" width = "500" height = "300"></svg>
<script>
  var svg = d3.select("#bar"),
    margin = {top: 20, right: 20, bottom: 20, left: 20},
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom,
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var x = d3.scaleBand()
    .rangeRound([0, width])
    .padding(0.08);

  var y = d3.scaleLinear()
    .range([height, 0]);

  var color = d3.scaleOrdinal()
    .range(["#7fc97f", "#beaed4", "#fdc086", "#ffff99"]);

  d3.csv("data.csv", function(d, i, columns) {
    for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
    d.total = t;
    return d;
  }, function(error, data) {
    if (error) throw error;

  var keys = data.columns.slice(1);

  x.domain(data.map(function(d) { return d.Year; }));
  y.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
  color.domain(keys);

  g.append("g")
    .selectAll("g")
    .data(d3.stack().keys(keys)(data))
    .enter().append("g")
      .attr("fill", function(d) { return color(d.key); })
    .selectAll("rect")
    .data(function(d) { return d; })
    .enter().append("rect")
      .attr("x", function(d) { return x(d.data.Year); })
      .attr("y", function(d) { return y(d[1]); })
      .attr("height", function(d) { return y(d[0]) - y(d[1]); })
      .attr("width", x.bandwidth());

  rect = g.selectAll("rect");
});

  d3.selectAll("input")
    .on("change", changed);

  function changed() {
    if (this.value === "step1") transitionStep1();
    else if (this.value === "step2") transitionStep2();
  }

  function transitionStep1() {
    rect.transition()
      .attr("y", function(d) { return y(d[1]); })
      .attr("x", function(d) { return x(d.data.Year); })
      .attr("width", x.bandwidth())
      .attr("stroke", "green");
  }

  function transitionStep2() {
    rect.transition()
      .attr("x", function(d) { return x(d.data.Year) + x.bandwidth() / 7 * 1; })
      .attr("width", x.bandwidth() / 7)
      .attr("y", function(d) { return y(d[1] - d[0]); })
      .attr("stroke", "red");
  }
</script></body></html>

还有 csv 文件:

Year,A,B,C,D
1995,60,47,28,39
1996,29,56,99,0
1997,30,26,63,33
1998,37,16,48,0
1999,46,49,64,21
2000,78,88,81,57
2001,18,11,11,64
2002,91,76,79,64
2003,30,99,96,79

【问题讨论】:

    标签: javascript csv d3.js charts visualization


    【解决方案1】:

    您所指的示例具有可将图表分组的线性值,因此.attr("x", function(d, i) { return x(i) + x.bandwidth() / n * this.parentNode.__data__.key; }) 可以正常工作。

    在您的情况下,columns/keys 不是线性比例,而是您必须设置比例的序数值集:

    参考simple d3 grouped bar chart

    要做到这一点,即设置一个序数比例,可以这样做:

    var x1 = d3.scaleBand();
    x1.domain(keys).rangeRound([0, x.bandwidth()]);
    

    因此,这个新规模将具有x 规模的带宽范围,其域为["A", "B", "C", "D"]。使用此比例设置rects 的x 属性对它们进行分组:

    .attr("x", function(d) { 
      return x(d.data.Year) + x1(d3.select(this.parentNode).datum().key); 
    })
    

    其中d3.select(this.parentNode).datum().key 表示列名。

    这里是JSFIDDLE(我使用d3.csvParse 来解析数据,但我相信您会明白这里的意思。这只是您需要重置的x 属性。

    这是一个使用文件的Plunkr

    这里还有一个代码 sn-p:

      var svg = d3.select("#bar"),
        margin = {top: 20, right: 20, bottom: 20, left: 20},
        width = +svg.attr("width") - margin.left - margin.right,
        height = +svg.attr("height") - margin.top - margin.bottom,
        g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
      var x = d3.scaleBand()
        .rangeRound([0, width])
        .padding(0.08);
    
    	var x1 = d3.scaleBand();
      
      var y = d3.scaleLinear()
        .range([height, 0]);
    
      var color = d3.scaleOrdinal()
        .range(["#7fc97f", "#beaed4", "#fdc086", "#ffff99"]);
    
    var csv = 'Year,A,B,C,D\n1995,60,47,28,39\n1996,29,56,99,0\n1997,30,26,63,33\n1998,37,16,48,0\n1999,46,49,64,21\n2000,78,88,81,57\n2001,18,11,11,64\n2002,91,76,79,64\n2003,30,99,96,79';
    
    var data = d3.csvParse(csv), columns = ["A", "B", "C", "D"];
    
    data.forEach(function(d) {
        for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
        d.total = t;
    });
    
      var keys = columns;
    
      x.domain(data.map(function(d) { return d.Year; }));
      x1.domain(keys).rangeRound([0, x.bandwidth()]);
      y.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
      color.domain(keys);
    
      g.append("g")
        .selectAll("g")
        .data(d3.stack().keys(keys)(data))
        .enter().append("g")
          .attr("fill", function(d) { return color(d.key); })
        .selectAll("rect")
        .data(function(d) { return d; })
        .enter().append("rect")
          .attr("x", function(d) { return x(d.data.Year); })
          .attr("y", function(d) { return y(d[1]); })
          .attr("height", function(d) { return y(d[0]) - y(d[1]); })
          .attr("width", x.bandwidth());
    
      rect = g.selectAll("rect");
    
      d3.selectAll("input")
        .on("change", changed);
    
      function changed() {
        if (this.value === "step1") transitionStep1();
        else if (this.value === "step2") transitionStep2();
      }
    
      function transitionStep1() {
        rect.transition()
          .attr("y", function(d) { return y(d[1]); })
          .attr("x", function(d) { return x(d.data.Year); })
          .attr("width", x.bandwidth())
          .attr("stroke", "green");
      }
      
    	function transitionStep2() {
        rect.transition()
          .attr("x", function(d) { 
          	return x(d.data.Year) + x1(d3.select(this.parentNode).datum().key); 
        	})
          .attr("width", x.bandwidth() / 7)
          .attr("y", function(d) { return y(d[1] - d[0]); })
          .attr("stroke", "red");
      }
    <!DOCTYPE html>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <html><body>
    
    <form>
      <label><input type="radio" name="mode" style="margin-left: 10" value="step1" checked>1</label>
      <label><input type="radio" name="mode" style="margin-left: 20" value="step2">2</label>
    </form>
    
    <svg id = "bar" width = "500" height = "300"></svg>

    希望有所帮助。 :)

    【讨论】:

    • 非常感谢!您提供了完整的代码,并解释了我哪里出错了。非常感谢您的时间和帮助。
    • 很高兴能帮上忙!
    【解决方案2】:

    您必须将键的索引传递给各个矩形,并使用此索引与减少的带宽相乘。如果您还使用键数组的长度(而不是 7),那么您与 CSV 列数无关。您需要将keys 变量的声明放在d3.csv 处理程序之外。

    你忘了把最初的矩形画成绿色。

    <script>
    var svg = d3.select("#bar"),
        margin = {top: 20, right: 20, bottom: 20, left: 20},
        width = +svg.attr("width") - margin.left - margin.right,
        height = +svg.attr("height") - margin.top - margin.bottom,
        g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
    var x = d3.scaleBand()
        .rangeRound([0, width])
        .padding(0.08);
    
    var y = d3.scaleLinear()
        .range([height, 0]);
    
    var color = d3.scaleOrdinal()
        .range(["#7fc97f", "#beaed4", "#fdc086", "#ffff99"]);
    
    var keys;
    
    d3.csv("/data.csv", function(d, i, columns) {
        for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
        d.total = t;
        return d;
    }, function(error, data) {
        if (error) throw error;
    
        keys = data.columns.slice(1);
    
        x.domain(data.map(function(d) { return d.Year; }));
        y.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
        color.domain(keys);
    
        var stackData = d3.stack().keys(keys)(data);
        stackData.forEach(element => {
            var keyIdx = keys.findIndex(e => e === element.key);
            element.forEach(e2 => { e2.keyIdx = keyIdx; });
        });
    
        g.append("g")
        .selectAll("g")
        .data(stackData)
        .enter().append("g")
            .attr("fill", function(d) { return color(d.key); })
        .selectAll("rect")
        .data(function(d) { return d; })
        .enter().append("rect")
            .attr("x", function(d) { return x(d.data.Year); })
            .attr("y", function(d) { return y(d[1]); })
            .attr("height", function(d) { return y(d[0]) - y(d[1]); })
            .attr("width", x.bandwidth())
            .attr("stroke", "green");
    
        rect = g.selectAll("rect");
    });
    
    d3.selectAll("input")
        .on("change", changed);
    
    function changed() {
        if (this.value === "step1") transitionStep1();
        else if (this.value === "step2") transitionStep2();
    }
    
    function transitionStep1() {
        rect.transition()
        .attr("y", function(d) { return y(d[1]); })
        .attr("x", function(d) { return x(d.data.Year); })
        .attr("width", x.bandwidth())
        .attr("stroke", "green");
    }
    
    function transitionStep2() {
        rect.transition()
        .attr("x", function(d, i) { return x(d.data.Year) + x.bandwidth() / (keys.length+1) * d.keyIdx; })
        .attr("width", x.bandwidth() / (keys.length+1))
        .attr("y", function(d) { return y(d[1] - d[0]); })
        .attr("stroke", "red");
    }
    </script>
    

    【讨论】:

    • 成功了,非常感谢您的帮助,感谢您关注绿色中风!
    【解决方案3】:

    我更新了您的示例以使其正常工作, 见https://jsfiddle.net/mc5wdL6s/84/

      function transitionStep2() {
        rect.transition()
         .duration(5500)
          .attr("x", function(d,i) { 
            console.log("d",d);
            console.log("i",i);
          return x(d.data.Year) + x.bandwidth() / m / n * i; })
          .attr("width", x.bandwidth() / n)
          .attr("y", function(d) { return y(d[1] - d[0]); })
          .attr("height", function(d) { return y(0) - y(d[1] - d[0]); })
          .attr("stroke", "red");
      }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多