如何处理d3.layout.stack()中缺少数据点的图层

Bre*_*ler 23 d3.js

我正在使用d3.stack来创建堆积区域图表,但如果我在每个图层中没有相同数量的项目,则会出现错误.我开始使用这样的数据数组:

[  
   {key:'Group1',value,date},  
   {key:'Group1',value,date},  
   {key:'Group1',value,date},  
   {key:'Group2',value,date},  
   {key:'Group2',value,date}  
]
Run Code Online (Sandbox Code Playgroud)

在我通过nest()和stack()运行之后,我最终得到了这种格式,正如预期的那样:

[  
   {key: 'Group1',  
    values: [ {key,value,date}, {key,value,date}, {key,value,date} ] },  
   {key: 'Group2',  
    values: [ {key,value,date}, {key,value,date} ]  }  
]
Run Code Online (Sandbox Code Playgroud)

我稍微修改了一个堆积区域样本来演示这个jsFiddle中的问题:http://jsfiddle.net/brentkeller/rTC3c/2/

如果删除sourceData数组中的任何一个数据点,您将在控制台中看到错误消息"无法读取未定义的属性'1'".

有没有办法让d3.stack假设缺少数据点的零值?如果没有,是否有一个优雅的解决方案来填补缺失的值?

小智 17

这不是d3特定的,而是用于填充键控数据阵列中的间隙的一般解决方案.我修改你的jsfiddle 这里具有以下功能:

function assignDefaultValues( dataset )
{
    var defaultValue = 0;
    var keys = [ 'Group1' , 'Group2', 'Group3' ];
    var hadData = [ true, true, true];
    var newData = [];
    var previousdate = new Date();
    var sortByDate = function(a,b){ return a.date > b.date ? 1 : -1; };

    dataset.sort(sortByDate);
    dataset.forEach(function(row){
        if(row.date.valueOf() !== previousdate.valueOf()){
            for(var i = 0 ; i < keys.length ; ++i){
                if(hadData[i] === false){
                    newData.push( { key: keys[i], 
                                   value: defaultValue, 
                                   date: previousdate });
                }
                hadData[i] = false;
            }
            previousdate = row.date;
        }
        hadData[keys.indexOf(row.key)] = true; 
    });
    for( i = 0 ; i < keys.length ; ++i){
        if(hadData[i] === false){
            newData.push( { key: keys[i], value: defaultValue, 
                            date: previousdate });
        }
    }
    return dataset.concat(newData).sort(sortByDate);
}
Run Code Online (Sandbox Code Playgroud)

它遍历给定的数据集,并且每当遇到新date值时,都会为keys尚未看到的任何数据集分配默认值.


Ber*_*ema 5

Stack确实是它所说的,堆叠图形,因此您作为用户负责以正确的格式提供数据.如果你考虑它,这是有道理的,因为堆栈基本上是数据格式不可知的.它提供了很大的灵活性,唯一的限制是每层可以访问相同数量的点.如何确定缺少哪些点?鉴于第一层有五个点而第二层有十个点,第一层是否缺少五个点?或者都是图层缺失点,因为第三层包含更多点.然后,如果缺少点,哪些?在开始时,最后,在中间的某个地方?同样,堆栈实现没有明智的方法来解决这个问题(除非它会强制非常严格的数据结构).

那么,你能做什么呢?我想你可以.我不能给你一个完整的实现,但可以给你一些正确方向的指示.我们从这里开始:

var stack = d3.layout.stack()
  .offset("zero")
  .values(function(d) { return d.values; })
Run Code Online (Sandbox Code Playgroud)

在这里,您只需返回值,在您的示例中,这将是嵌套运算符的结果.因此,此时您可以"修复"这些值.

您需要做的第一件事是确定观察的最大数量.

var nested = nest.entries(data);
var max = nested.reduce(function(prev, cur) {
  return Math.max(prev, cur.values.length);
}, 0);
Run Code Online (Sandbox Code Playgroud)

现在是棘手的部分.一旦知道元素的最大数量,就需要调整传递给值的函数.在这里,您必须对数据做出假设.从你的问题我明白,对于一些群体,价值观缺失.所以有两种可能性.您可以假设具有最大元素数量的组包含范围内的所有项目,或者您假设某个范围,并检查所有组中是否包含您范围内每个"tick"的值.因此,如果您的范围是一个日期范围(如您的示例中)并且您希望每天(或者对于该问题的任何间隔)进行测量,那么您将必须遍历组中的项目并自己填补空白.我将尝试给出一个(未经测试的)数值范围的例子:

// define some calculated values that can be reused in correctedValues
var range = [0, 1];
var step = 0.1;

function correctedValues(d) {
  var values = d.values;
  var result = [];
  var expected = 0;
  for (var i = 0; i < values.length; ++i) {
     var value = values[i];
     // Add null-entries
     while (value.x > expected) {
       result.push({x: expected, otherproperties_you_need... });
       expected += step;
     }
     result.push(value); // Now add the real data point.
     expected = value.x;
  }

  // Fill up the end of of the array if needed
  while(expected < range[1]) {
    result.push({x: expected, otherproperties_you_need... });
    expected += step;
  }
  return result;
}

// Now use our costom function for the stack
var stack = d3.layout.stack()
 .offset("zero")
 .values(correctedValues)
...
Run Code Online (Sandbox Code Playgroud)

如上所述,这部分未经测试,并没有直接解决您的问题(因为我使用的是数值范围),但我认为它应该让您了解如何解决您的问题(以及问题的实际来源是什么).