D3:在有多条线的折线图中跳过空值

8

我有一个动态数组来显示一条线图和几条线。例如:

var data = 
[[{x:2005, y:100}, {x:2007, y:96.5}, {x:2009, y:100.3}, {x:2011, y:102.3}], 
 [{x:2005, y:100}, {x:2007, y:105},  {x:2009, y:102},   {x:2011, y:104}]]

我的脚本的这一部分将会绘制线条:

graph.selectAll("path.line")
.data(data)
.enter().append("path")
.attr("class", "line")
.style("stroke", function(d, i) { return d3.rgb(z(i)); })
.style("stroke-width", 2)
.attr("d", d3.svg.line()
.y(function(d) { return y(d.y); })
.x(function(d,i) { return x(i); }));

我是一名有用的助手,可以为您翻译文本。
(我使用的脚本基于 http://cgit.drupalcode.org/d3/tree/libraries/d3.linegraph/linegraph.js
我的问题是:数据数组是动态的,我事先不知道它里面有什么。有时2005年的y值将为空:
var data = 
[[{x:2005, y:100},  {x:2007, y:96.5}, {x:2009, y:100.3}, {x:2011, y:102.3}], 
 [{x:2005, y:null}, {x:2007, y:105},  {x:2009, y:102},   {x:2011, y:104}]]

我该如何让第二行忽略第一个对象,从2007年开始呢?
根据答案1的建议,现在我的代码是这样的,但仍然显示整行:
data = 
[[{x:2005, y:100},  {x:2007, y:96.5}, {x:2009, y:100.3}, {x:2011, y:102.3}], 
 [{x:2005, y:null}, {x:2007, y:105},  {x:2009, y:102},   {x:2011, y:104}]];

var validatedInput = function(inptArray) { 
 return inptArray.filter(function(obj) {
  return obj.y != null;
 });
};

graph.selectAll("path.line")
    .data(data, validatedInput)
  .enter().append("path")
    .attr("class", "line")
    .style("stroke", function(d, i) { return d3.rgb(z(i)); })
    .style("stroke-width", 2)
    .attr("d", d3.svg.line()
    .y(function(d) { return y(d.y); })
    .x(function(d,i) { return x(i); }));
2个回答

8

最终,我自己解决了这个问题,参考了这里的解决方案。窍门在于尽可能晚地删除空值,这样画布上所有数值(点)的位置都能保持不变。

graph.selectAll("path.line")
    .data(data)
  .enter().append("path")
    .attr("class", "line")
    .style("stroke", function(d, i) { return d3.rgb(z(i)); })
    .style("stroke-width", 2)
    .attr("d", d3.svg.line()
    .y(function(d) { return y(d.y); })
    .defined(function(d) { return d.y; }) // Omit empty values.
    .x(function(d,i) { return x(i); }));

这将适用于行首和行尾的空值。


嘿 @eingebruiker,你能解释一下 defined() 这行代码是如何工作的,并且让你省略空值吗? - user3768495
@user3768495 哎呀,那已经是一年多以前的事情了,我自那以后就没有再看过它 :-) 我相信它的工作原理是这样的:它充当一个过滤器。看看最后三行。首先确定画布上所有点的y位置。然后,通过使用defined(),删除y == null的点。然后确定剩余点的x位置。 - eindgebruiker

2
这应该可以做到:
.data(data, function(inptArray) { 
  return inptArray.filter(function(obj) {
   return obj.y != null;
  }) 
});

不过最好的写法应该是这样:

var validatedInput = function(inptArray) { 
 return inptArray.filter(function(obj) {
  return obj.y != null;
});

.data(data, validatedInput);

或者,您可以在将数据对象提供给D3之前对其进行格式化:

var data = data.map(function(obj){
 return obj.filter(function(obj) {
  return obj.y != null;
 })
})

嗨,sergeyz,我尝试了你的建议,但现在第二行根本没有生成。 - eindgebruiker
哦,抱歉,我匆忙地从Firebug的console.log中复制了这个。实际上,数据是对象数组的数组。我已经使用正确的数据数组修改了我的问题。 - eindgebruiker
我尝试了您的validateInput(在末尾添加了一个缺失的})。现在我有两行完整的代码,包括空值。所以它还没有起作用。 - eindgebruiker
奇怪,我的操作没问题。尝试另一种方法。刚刚把它加入了我的答案。 - sergeyz
是的,我也尝试过最后一种方法。它确实会跳过第一个对象,但整行都向左移动,所以它将从2005开始,到2009结束。 - eindgebruiker
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接