简体   繁体   English

d3嵌套分组条形图

[英]d3 nested grouped bar chart

First of all sorry if my English is difficult to understand, I'll try my best... 首先抱歉,如果我的英语很难听懂,我会尽力而为...

I am rather new to D3.js and I'm trying to create a D3 grouped bar chart using nested data. 我是D3.js的新手,我正尝试使用嵌套数据创建D3分组条形图。 I have looked at some solutions shared here, but they only show one-level grouping. 我看过这里共享的一些解决方案,但它们仅显示了一级分组。 In my case, data will come from a csv file that has this data structure: 就我而言,数据将来自具有以下数据结构的csv文件:

groups,categories,value 1,value 2,value 3
group 1,A,61.0158803,25.903359,13.08076071
group 1,B,71.27703826,21.0180133,7.70494844
group 1,C,82.70203982,13.52731445,3.770645737
group 2,A,58.85721523,28.25939061,12.88339417
group 2,B,71.39695487,20.66010982,7.942935308
group 2,C,82.22389321,13.68924542,4.08686137

The chart is intended to have two x axis, one for the groups (level 0) and one for the categories (level 1).Values 1 to 3 will display as grouped bars for each catergory, and the categories will be displayed within the corresponding group. 该图表旨在具有两个x轴,一个用于组(级别0),一个用于类别(级别1)。值1至3将显示为每个类别的分组条形图,并且类别将显示在相应的类别中组。

The structure of the chart should be: 图表的结构应为:

value 1 | value 2 | value 3 | value 1 | value 2 | value 3 | value 1 | value 2 | value 3 |
|        category A         |          category B         |          category C         |
|                                       group 1                                         |

and the same for group 2, placed contiguous. 与第2组相同,相邻。

The problem is with the code I am working on, I get the right axis but data corresponding two both groups are shown, one on top of the other, in each group area. 问题出在我正在处理的代码上,我得到了右轴,但是在每个组区域中,显示了对应于两组的数据,一组在另一组之上。 I am not able to link the data on the categories to their corresponding group in orther to draw them where it corresponds. 我无法将类别上的数据链接到其相应的组,否则无法将它们绘制在相应的位置。

Here is the code I've got so far: 这是到目前为止我得到的代码:

var x0 = d3.scale.ordinal()
  .rangeRoundBands([0,width], 0);

var x1 = d3.scale.ordinal()
.rangeRoundBands([0,width]);

var x2 = d3.scale.ordinal();

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

var color = d3.scale.category10();

var x0Axis = d3.svg.axis()
  .scale(x0)
  .orient("bottom");

var x1Axis = d3.svg.axis()
  .scale(x1)
 .orient("bottom");

var yAxis = d3.svg.axis()
  .scale(y)
  .orient("left");

var svg = d3.select(".chart")
  .append("svg")
  .attr("class", "svg")
  .attr("viewBox", "" + margin* -1 + " " + margin* -1 + " " + (width + margin*2) + " " + (height + margin *2) + "")
  .attr ("preserveAspectRatio", "xMidYMid")
  .attr("width", "100%")
  .attr("height", "100%")



d3.csv("../data/EQ01.csv", function(error, data){
  if (error) throw error;

 var seriesNames = d3.keys(data[0]).filter(function(key) { return key !== "categories" && key !== "groups";});

  data.forEach(function(d) {
 d.values = seriesNames.map(function(name) { return {
     xValue: name,
     yValue: +d[name]
   };
 });
   });

   nested = d3.nest()
      .key(function(d) { return d.groups})
      .key(function(d) { return d.categories})
      .entries(data);

  y.domain([0, d3.max(data, function(d) { return d3.max(d.values, function(d) { return d.yValue; }); })]);
  x0.domain(nested.map(function(d) {return d.key;}));
  x1.domain(data.map(function(d) { return d.categories; })).rangeRoundBands([0, x0.rangeBand() ], 0.1);
  x2.domain(seriesNames).rangeRoundBands([0, x1.rangeBand()], 0);

  svg.append("g")
    .attr("class", "x0 axis")
    .attr("transform", "translate(0," + (height+30) + ")")
    .call(x0Axis);

  svg.append("g")
   .attr("class", "y axis")
   .call(yAxis)

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

  group.append("g")
   .attr("class", "x1 axis")
   .attr("transform", "translate(0," + height + ")")
   .call(x1Axis);

  var category = group.selectAll(".category")
   .data(data)
   .enter().append("g")
   .attr("class", "category")
   .attr("transform", function(d) { return "translate(" + x1(d.categories) + ",0)"; });

  category.selectAll("rect")
  .data(function(d) { return d.values; })
  .enter().append("rect")
  .attr("width", x2.rangeBand())
  .attr("x", function(d) { return x2(d.xValue); })
  .attr("y", function(d) { return y(d.yValue); })
  .attr("height", function(d) { return height - y(d.yValue); })
  .style("fill", function(d){return color(d.xValue)})

Many thanks in advance for the help! 预先非常感谢您的帮助!

The issue is that you are not joining correctly your data with your elements. 问题是您没有将数据与元素正确连接。

We need to build different scales in order to obtain the correct rangeBand value. 我们需要建立不同的标度以获得正确的rangeBand值。

var x_groups = d3.scale.ordinal()
  .rangeRoundBands([0, width], .1);

var x_categories = d3.scale.ordinal();

var x_values = d3.scale.ordinal();

I created a nested data structure that will contain everything we need for our grouped bar chart approach. 我创建了一个嵌套的数据结构,其中将包含我们的分组条形图方法所需的所有内容。

var nested = d3.nest()
    .key(function(d) {
      return d.groups;
    })
    .key(function(d) {
      return d.categories;
    })
    .rollup(function(leaves) {
      return [{
        key: 'v-a',
        value: leaves[0]['value 1']
      }, {
        key: 'v-b',
        value: leaves[0]['value 2']
      }, {
        key: 'v-c',
        value: leaves[0]['value 3']
      }];
    })
    .entries(data);

Next lets configure our scales with the information we just got. 接下来,使用我们刚得到的信息来配置秤。

  x_groups.domain(nested.map(function(d) {
    return d.key;
  }));
  //var categories = ['A', 'B', 'C']; 
  var categories = nested[0].values.map(function(d, i) {
    return d.key;
  });
  x_categories.domain(categories).rangeRoundBands([0, x_groups.rangeBand()]);
  //var values = ['value 1', 'value 2', 'value 3']; 
  var values = nested[0].values[0].values.map(function(d, i) {
    return d.key;
  });
  x_values.domain(values).rangeRoundBands([0, x_categories.rangeBand()]);

Then we can finally start our data join. 然后,我们终于可以开始我们的数据联接了。 You can see that when we enter a new level of info we need to set the data function correctly. 您可以看到,当我们输入新的信息级别时,我们需要正确设置data功能。

var groups_g = svg.selectAll(".group")
  .data(nested)
  .enter().append("g")
  .attr("class", function(d) {
    return 'group group-' + d.key;
  })
  .attr("transform", function(d) {
    return "translate(" + x_groups(d.key) + ",0)";
  });

var categories_g = groups_g.selectAll(".category")
  .data(function(d) {
    return d.values;
  })
  .enter().append("g")
  .attr("class", function(d) {
    return 'category category-' + d.key;
  })
  .attr("transform", function(d) {
    return "translate(" + x_categories(d.key) + ",0)";
  });

var categories_labels = categories_g.selectAll('.category-label')
  .data(function(d) {
    return [d.key];
  })
  .enter().append("text")
  .attr("class", function(d) {
    return 'category-label category-label-' + d;
  })
  .attr("x", function(d) {
    return x_categories.rangeBand() / 2;
  })
  .attr('y', function(d) {
    return height + 25;
  })
  .attr('text-anchor', 'middle')
  .text(function(d) {
    return d;
  })

var values_g = categories_g.selectAll(".value")
  .data(function(d) {
    return d.values;
  })
  .enter().append("g")
  .attr("class", function(d) {
    return 'value value-' + d.key;
  })
  .attr("transform", function(d) {
    return "translate(" + x_values(d.key) + ",0)";
  });

var values_labels = values_g.selectAll('.value-label')
  .data(function(d) {
    return [d.key];
  })
  .enter().append("text")
  .attr("class", function(d) {
    return 'value-label value-label-' + d;
  })
  .attr("x", function(d) {
    return x_values.rangeBand() / 2;
  })
  .attr('y', function(d) {
    return height + 10;
  })
  .attr('text-anchor', 'middle')
  .text(function(d) {
    return d;
  })

var rects = values_g.selectAll('.rect')
  .data(function(d) {
    return [d];
  })
  .enter().append("rect")
  .attr("class", "rect")
  .attr("width", x_values.rangeBand())
  .attr("x", function(d) {
    return 0;
  })
  .attr("y", function(d) {
    return y(d.value);
  })
  .attr("height", function(d) {
    return height - y(d.value);
  })
  .style("fill", function(d) {
    return color(d.key);
  });

Working plnkr: https://plnkr.co/edit/qGZ1YuyFZnVtp04bqZki?p=preview 工作plnkr: https ://plnkr.co/edit/qGZ1YuyFZnVtp04bqZki ? p = preview

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

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