d3 v4从CSV堆叠到分组条形图

时间:2018-07-24 19:50:52

标签: javascript csv d3.js charts visualization

参考文献Mike Bostick's Stacked to Grouped Bar Chart example,我正在对其进行修改以与CSV文件配合使用。我已经为此工作了两周,并且浏览了Stack Overflow和其他地方的无数示例,并感到困惑。

堆叠的条形图有效。

堆积条形图:

Stacked Bar Chart

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

分组条形图:

Grouped Bar Chart

在功能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

3 个答案:

答案 0 :(得分:0)

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

在您的情况下,列/键不是线性比例尺,而是您必须为其设置比例尺的序数集:

引用simple d3 grouped bar chart

要做到这一点,即设置序数刻度,可以执行以下操作:

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

因此,此新秤的范围为x,范围为["A", "B", "C", "D"]秤的带宽。使用此比例尺来设置x的{​​{1}}属性以对其进行分组:

rect

其中.attr("x", function(d) { return x(d.data.Year) + x1(d3.select(this.parentNode).datum().key); }) 代表列名。

这里是JSFIDDLE(我用d3.select(this.parentNode).datum().key来解析数据,但是我敢肯定,您会在这里找到要点。这只是您需要重置的d3.csvParse属性。

这是一个使用文件的Plunkr

这也是一个代码段:

x
  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");
  }

希望有帮助。 :)

答案 1 :(得分:0)

您必须将键的索引传递到各个矩形,并使用此索引乘以减小的bandwith。如果您还使用keys数组的长度(而不是7),则与CSV列数无关。您需要将if (table.rows[1].cells[0].value === undefined) { 变量的声明放在keys处理程序之外。

您忘了将初始矩形标记为绿色。

d3.csv

答案 2 :(得分:0)

我更新了您的示例,以使其起作用, 参见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");
  }