我正在使用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尚未看到的任何数据集分配默认值.
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)
如上所述,这部分未经测试,并没有直接解决您的问题(因为我使用的是数值范围),但我认为它应该让您了解如何解决您的问题(以及问题的实际来源是什么).