d3嵌套分组的条形图 [英] d3 nested grouped bar chart

查看:182
本文介绍了d3嵌套分组的条形图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先很抱歉,如果我的英语很难理解,我会尽我所能...



我对D3.js很新,尝试使用嵌套数据创建D3组条形图。我看了一些在这里共享的解决方案,但他们只显示一级分组。在我的情况下,数据将来自具有此数据结构的csv文件:

 组,类别,值1,值2 ,值3 
组1,A,61.0158803,25.903359,13.08076071
组1,B,71.27703826,21.0180133,7.70494844
组1,C,82.70203982,13.52731445,3.770645737
组2,A,58.85721523,28.25939061,12.88339417
组2,B,71.39695487,20.66010982,7.942935308
组2,C,82.22389321,13.68924542,4.08686137
pre>

图表旨在具有两个x轴,一个用于组(级别0),一个用于类别(级别1).Values 1至3将显示



图表的结构应为:

  value 1 |值2 |值3 |值1 |值2 |值3 |值1 |值2 |值3 | 
|类别A |类别B |类别C |
|组1 |

,第2组也相同。


$ b b

问题是我的代码,我得到正确的轴,但数据对应的两个组都显示,一个在另一个之上,在每个组区域。我无法将类别上的数据链接到他们相应的组中,以绘制它们对应的地方。



以下是我到目前为止的代码:

  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%)
b

$ b 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数据,函数(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 (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){returntranslate(+ x0(d.key)+,0);});

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

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

category.selectAll(rect)
.data(function(d){return d.values;})
.enter
.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)})

解决方案

问题是你没有用你的元素正确地加入你的数据。 / p>

我们需要构建不同的缩放以获得正确的 rangeBand 值。

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

var x_categories = d3.scale.ordinal();

var x_values = d3.scale.ordinal();

我创建了一个嵌套数据结构,它将包含我们分组的条形图方法所需的所有内容。 p>

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

接下来可以使用刚刚获得的信息配置秤。

  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()]);

然后我们可以开始我们的数据加入。您可以看到,当我们输入一个新的信息级别时,我们需要正确设置数据

  var groups_g = svg.selectAll(。group)
.data(nested)
.enter()。append(g)
.attr (class,function(d){
return'group group-'+ d.key;
})
.attr(transform,function(d){
returntranslate(+ 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){
returntranslate(+ 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;
})
。 ,function(d){
return height + 25;
})
.attr('text-anchor','middle')
.text
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){
returntranslate(+ 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 ,function(d){
return height + 10;
})
.attr('text-anchor','middle')
.text
return d;
})

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

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


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

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.

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.

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.

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.

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

这篇关于d3嵌套分组的条形图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆