d3 v4嵌套数据和堆积条形图 [英] d3 v4 nested data and stacked bar chart

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

问题描述

我正在尝试制作一个如下所示的图表:

我有一个D3.nest数据结构,如下所示:

  {键":区域1",值":[{"key":"5.验证完成",值":12.5},{"key":"Deferred","value":1},{"key":"3.识别并验证建议的解决方案","value":5},{"key":"1.定义并描述问题或机会","value":0}]},{键":区域2",值":[{"key":"5.验证完成","value":41.2},{"key":"4.实施解决方案","value":86.6},{"key":"3.识别并验证提议的解决方案","value":6},{"key":"2.找出根本原因","value":4},{"key":"1.定义并描述问题或机会","value":9}]}},{键":区域3",值":[{"key":"5.验证完成","value":40},{"key":"4.实施解决方案","value":49.2},{"key":"3.识别并验证建议的解决方案","value":10.4}]},{键":区域4",值":[{"key":"Deferred","value":0.25},{"key":"4.实施解决方案","value":28},{"key":"3.识别并验证建议的解决方案","value":84.9},{"key":"2.确定根本原因","value":0}]} 

我的zKeys的结构为:

我没有成功尝试

我可能已经做了一个非常round回的方法,但这是我为解决d3.stack()没有正确数据结构的问题而做的事情.

1)我保留了d3.nest()输出的用法,因为它允许我将每个值汇总为每个元素的单个键/值对,如下所示:

2)然后,我使用以下代码对数据输出进行了清理,以使其在所有示例中都看起来像d3.csv()的输出(为丢失的数据添加键/值默认值并展平结构:

 //BEGIN d3.stack的数据清理//为缺失的数据点添加默认值,以使每个数组的格式相同nestData = nestData.map(function(keyObj){返回 {键:keyObj.key,值:zKeys.map(function(k){值= keyObj.values.filter(function(v){return v.key == k;})[0];返回值||({key:k,value:0});})};});//遍历嵌套数组并创建一个新的array元素,该元素将每个单独的嵌套元素转换为单个对象中的键/值对.var flatData = [];nestData.forEach(function(d){var obj = {区域:d.key}d.values.forEach(function(f){obj [f.key] = f.value;});flatData.push(obj);});//结束d3.stack的数据清理 

数据现在看起来像这样:

3)清除数据后,我便可以像下面这样直接使用Mike的示例中的代码:

  x.domain(flatData.map(function(d){return d.Area;}));y.domain([0,d3.max(nestData,function(d){返回d3.sum(d.values,function(d){return d.value})})+ 20]));z.domain(zKeys)var g = svg.append("g").attr("transform","translate(" + margin.left +," + margin.top +)");g.append("g").selectAll("g").data(d3.stack().keys(zKeys)(flatData)).enter().append("g").attr("fill",function(d){return z(d.key);}).selectAll("rect").data(function(d){return d;}).enter().append("rect").attr("x",function(d){return x(d.data.Area);}).attr("y",function(d){return y(d [1]);}).attr("height",function(d){return y(d [0])-y(d [1]);}).attr("width",x.bandwidth()); 

I am trying to make a chart that looks like this:

I have a D3.nest data structure that looks like this:

{"key":"Area 1","values":[
    {"key":"5. Validation Complete","value":12.5},
    {"key":"Deferred","value":1},
    {"key":"3. Identify & Validate Proposed Solutions","value":5},
    {"key":"1. Define & Describe the Problem or Opportunity","value":0}]},
{"key":"Area 2","values":[
    {"key":"5. Validation Complete","value":41.2},
    {"key":"4. Implement the Solutions","value":86.6},
    {"key":"3. Identify & Validate Proposed Solutions","value":6},
    {"key":"2. Identify Root Causes","value":4},
    {"key":"1. Define & Describe the Problem or Opportunity","value":9}]},
{"key":"Area 3","values":[
    {"key":"5. Validation Complete","value":40},
    {"key":"4. Implement the Solutions","value":49.2},
    {"key":"3. Identify & Validate Proposed Solutions","value":10.4}]},
{"key":"Area 4","values":[
    {"key":"Deferred","value":0.25},
    {"key":"4. Implement the Solutions","value":28},
    {"key":"3. Identify & Validate Proposed Solutions","value":84.9},
    {"key":"2. Identify Root Causes","value":0}]}

My zKeys is structured as:

I have unsuccessfully tried Bostock's Stacked Bar Chart Example and this SO post.

Here is my code:

    var svg = d3.select("svg"),
    margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom;

var x = d3.scaleBand().rangeRound([0, width]).padding(0.1);
var y = d3.scaleLinear().rangeRound([height, 0]);
var z = d3.scaleOrdinal().range(["#F8A11E", "#E51F36", "#582C85", "#1C92D0", "#017165", "#7F7F7F"]);    

var g = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var sharepointStatusArray = getListData("Points List","ID,Title,Color_Code");
var data=getListData("Points%20List","$select=Area,StatusID,Points,Status/Title&$expand=Status");   

var zKeys = [];

sharepointStatusArray.forEach(function(d)
 {
    zKeys.push(d.Title);
 });

var nestData = d3.nest()
    .key(function(d) { return d.Area; })
    .key(function(d) { return d.Status.Title; })
    .rollup(function(v) { return d3.sum(v, function(d) { return d.Points; }); })
    .entries(data);

    nestData.sort(function(a,b) {return b.total - a.total;});
    x.domain(nestData.map(function(d) { return d.key; }));
    y.domain([0, d3.max(nestData, function(d){return d3.sum(d.values, function(d){return d.value})})+20]).nice();
    z.domain(zKeys) 

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

  g.append("g")
    .attr("class", "axis axis--x")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x));

  g.append("g")
      .attr("class", "axis")
      .call(d3.axisLeft(y).ticks(null, "s"))
    .append("text")
      .attr("x", 2)
      .attr("y", y(y.ticks().pop()) + 0.5)
      .attr("dy", "0.32em")
      .attr("fill", "#000")
      .attr("font-weight", "bold")
      .attr("text-anchor", "start")
      .text("Hours");


    //Creating legend for colors
      var legend = g.append("g")
      .attr("font-family", "sans-serif")
      .attr("font-size", 10)
      .attr("text-anchor", "end")
    .selectAll("g")
    .data(zKeys.slice())
    .enter().append("g")
      .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

  legend.append("rect")
      .attr("x", width - 19)
      .attr("width", 19)
      .attr("height", 19)
      .attr("fill", z);

  legend.append("text")
      .attr("x", width - 24)
      .attr("y", 9.5)
      .attr("dy", "0.32em")
      .text(function(d) { return d; }); 

I cannot get the stacked bar chart to draw according to the examples. Almost every example out there for this uses d3.csv and not d3.nest so I am at a loss as to how those examples translate to a d3.nest scenario with an output array.

Can anyone please help me out? Thanks.

解决方案

I ended up finding the answer to my own question. One of the things I discovered was that all the information I found that dealt with d3.stack() stated that the data that was sent to the function needed to be 2D (2 dimensional). This was something that d3.nest() outputs nicely. This turned out to not be accurate. I should have done this from the beginning, but I debugged the example Mike Bostock’s Stacked Bar Chart and discovered that the output of d3.csv() that everyone uses in their examples actually outputs a 1D array with each element containing key/value pairs of the data to be displayed in each rect.

I've probably made a very roundabout way of doing it, but here is what I did to solve my problem of not having the right data structure for d3.stack().

1) I kept my usage of d3.nest() output because it allowed me to sum up individual values into single key/value pairs for each element like this:

2) I then sanitized that data output using the following code to get it to look like the output of the d3.csv() in all the examples (adding key/value defaults for missing data and flattening the structure:

//BEGIN data cleanup for d3.stack
//Add default values for missing data points to make each array formatted the same
    nestData = nestData.map(function(keyObj) {
        return {
            key: keyObj.key,
            values: zKeys.map(function(k) { 
                    value = keyObj.values.filter(function(v) { return v.key == k; })[0];
                    return value || ({key: k, value: 0});
                })
        };
    });

//Loop through the nested array and create a new array element that converts each individual nested element into a key/value pair in a single object.
var flatData = [];
nestData.forEach(function(d) {
    var obj = { Area: d.key }
        d.values.forEach(function(f) {
            obj[f.key] = f.value;
        });
    flatData.push(obj);
  });
//END data cleanup for d3.stack

The data now looked like this:

3) After the data sanitizing I was then able to use the code from Mike's example out of the box like so:

    x.domain(flatData.map(function(d) { return d.Area; }));
    y.domain([0, d3.max(nestData, function(d){return d3.sum(d.values, function(d){return d.value})})+20]);
    z.domain(zKeys) 

var g = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

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

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

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