简体   繁体   English

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

[英]d3 v4 Stacked to Grouped Bar Chart from CSV

Reference Mike Bostick's Stacked to Grouped Bar Chart example , I am modifying it to work with a CSV file. 参考Mike Bostick的“堆积到分组的条形图”示例 ,我正在对其进行修改以与CSV文件一起使用。 I have been working on this for a couple weeks and have looked through countless examples on Stack Overflow and elsewhere and am stumped. 我已经为此工作了两周,并且浏览了Stack Overflow和其他地方的无数示例,并感到困惑。

The stacked bar chart works. 堆积的条形图有效。

Stacked Bar Chart: 堆积条形图:

堆积条形图

When I transition to a grouped bar chart I am only having issues referencing the key or series that is stacked or grouped. 当我过渡到分组的条形图时,我只会遇到引用堆叠或分组的键或系列的问题。 Right now all the rectangles display on top of each other instead of next to each other. 现在,所有矩形都显示在彼此的顶部,而不是彼此相邻。

Grouped Bar Chart: 分组条形图:

分组条形图

In the function transitionStep2() I want to multiply by a number corresponding to the series or key. 在函数transitionStep2()我想乘以对应于序列或键的数字。 I am currently multiplying by the number 1 in this function as a placeholder .attr("x", function(d) { return x(d.data.Year) + x.bandwidth() / 7 * 1; }) . 我目前正在将此函数中的数字乘以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>

And the csv file: 和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

The example you're referring has linear values to group the chart into and so .attr("x", function(d, i) { return x(i) + x.bandwidth() / n * this.parentNode.__data__.key; }) works fine. 您所引用的示例具有将图表分组的线性值,因此.attr("x", function(d, i) { return x(i) + x.bandwidth() / n * this.parentNode.__data__.key; })工作正常。

In your case, the columns/keys is not a linear scale but an ordinal value set that you have to set a scale for: 在您的情况下, 列/键不是线性刻度,而是必须为以下项设置刻度的序数集:

Refer simple d3 grouped bar chart 参考简单的d3分组条形图

To do that ie set up a ordinal scale, here's what can be done: 为此,即设置序数刻度,可以执行以下操作:

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

So this new scale would have a range of the x scale's bandwidth with the domain of ["A", "B", "C", "D"] . 因此,此新刻度将具有x刻度的带宽范围,范围为["A", "B", "C", "D"] Using this scale to set the x attribute of the rect s to group them: 使用此比例尺将rectx属性设置为分组:

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

where d3.select(this.parentNode).datum().key represent the column name. 其中d3.select(this.parentNode).datum().key表示列名称。

Here's JSFIDDLE (I've used d3.csvParse to parse the data but I'm sure you'll get the point here. It's just the x attribute you need to reset. 这是JSFIDDLE (我使用d3.csvParse解析数据,但是我敢肯定,您会明白这里的意思。它只是需要重置的x属性。

Here's a Plunkr that uses a file. 这是使用文件的Plunkr

Here's a code snippet as well: 这也是一个代码片段:

  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> 

Hope something helps. 希望有帮助。 :) :)

You have to pass the index of the key to the individual rectangles and use this index to multiply with the reduced bandwith. 您必须将键的索引传递给各个矩形,并使用该索引乘以减小的bandwith。 If you also use the length of the keys array (instead of 7) you are CSV column count independent. 如果您还使用keys数组的长度(而不是7),则与CSV列数无关。 You need to put the declaration of keys variable outside the d3.csv handler. 您需要将keys变量的声明放在d3.csv处理程序之外。

You forgot to stroke the initial rects green. 您忘记抚摸初始的矩形绿色。

<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>

I updated your examples so that it works, see https://jsfiddle.net/mc5wdL6s/84/ 我更新了您的示例,使其可以正常工作,请参阅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");
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM