路径转换与差距 - 怎么做?

时间:2013-12-26 21:05:37

标签: d3.js

我绘制了一些有一些“间隙”的折线图(路径)。我在间隙之间使用了一个技巧,然后在我的数据间隙之间没有插值。

示例:

pos, value
1,10
2,15
3,20
8,5
9,6
10,20

正如你所看到的,我在第3和第8位之间有一个间隙。这里我要做的是创建一个值为0的位置4,以及一个值为0的位置7.然后,每当我绘制我的折线图时,我得到的我的差距是正确的图表。

现在,我需要在此折线图上进行转换。鉴于我有这种“差距黑客”,我的过渡并不是美好的:( 它以一种我不喜欢的方式移动线条。

我该怎么办? 在这种情况下是否可以进行转换而不需要在我的间隙之间有很多0值?

我不想为我不需要表示的位置添加0值,否则我的数据库会增加很多,我的可视化会很慢。

非常感谢任何见解。

1 个答案:

答案 0 :(得分:10)

在更新函数中,您需要做的第一件事是创建一个新的数据数组,填充您的位置变量的缺失值,同时保留值变量undefined。类似的东西:

var data = [/* Your original data table */];
       //results of d3.tsv or whatever you use

var posRange = d3.max(data, function(d){return d.pos;});  
    //get the max and min pos values as a two-element array
var dataWithGaps = new Array(posRange[1] + 1 - posRange[0] );
    //i.e., length of the array is the difference in position, 
    //plus 1 to include the last position 
    //(eg for positions 2-5 you need 4 elements 2,3,4,5, which is 5+1-2

var j=dataWithGaps.length;
while (j--){ //count down the length of the new array
    dataWithGaps[j] = {pos:posRange[0]+j, value:NaN}; 
        //create a valid position and undefined value
}
j=data.length;
while (j--){//scan through the data and copy over
    d = data[j];
    dataWithGaps[d.pos-posRange[0]].value = d.value; 
      //find the correct index in the new array, and set it's value
}

如果您的位置是从零或一开始计算的整数,那么只需使用数组的索引作为位置值,就可以大大简化此代码。如果您的位置是时间戳或其他内容,则代码会变得更复杂。

无论哪种方式,当您更新数据时,请务必检查范围是否已更改,因此您是否需要填充dataWithGaps数组的开头或结尾(使用push或{ {1}})有更多的位置值。然后重复最后两个循环。

现在要图表......

以下示例均以包含unshift(非数字)值作为间隙的数字数组开头,其中数组的索引用作x坐标。

第一个例子,也许你已经这样做了,就是使用d3路径生成器函数.defined(function(d){}) method。这允许您指定哪些数据点应该是未定义的,并且d3会自动将您的线分成未定义点周围的线段。

Ta-Dah! http://fiddle.jshell.net/9jTk4/2/

魔法代码是此链中的最后一个语句:

NaN

请注意,数据点是使用SVG line markers生成的,这样即使它们没有连接到任何东西也可以看到点,因为它们的邻居是未定义的。

(忏悔时间:我才发现之后我花了几个小时想出一个自定义解决方案。幸运的是,我的额外时间没有浪费,因为有一个缺陷d3默认方法......)

问题在于您添加转换。

Dum-da- DUM(不祥的器官和弦) http://fiddle.jshell.net/9jTk4/1/

D3无法知道从哪里开始转换的新线段。如果您只在两个现有点之间添加一个新点,则它会从前一点平滑过渡。但是,如果你有多个点添加到一个间隙,它会在数组中最后一个元素的位置启动它们 - 这可能会在你添加到行尾时起作用,但看起来很好看如果填补空白,那就太难看了。

可以使用自定义tween函数修复转换,但从头开始编写并不是一件容易的事。 (但是,如果有人想要创建一个,那么将它作为拉取请求添加到d3标准库可能是值得的。)

与此同时,请看一下我创建的这种替代方法(在我仔细阅读d3 API之前,发现它们已经考虑了差距)。

<强>钽大新! AlaKaZAM !!! http://fiddle.jshell.net/pwmA3/3/

它看起来与上一版本非常相似,除了平滑过渡,但实际上它在结构上非常不同。它不使用单个路径来表示整行,而是使用一系列var drawLine = d3.svg.line() .x(function(d,i) { return xScale(i); }) .y(function(d) { return yScale(d); }) .defined(function(d) {return !isNaN(d)}); //the line should be only be defined at points where the data value is not NaN //use !isNaN(d.value) if your data values are objects 元素,每个元素将路径绘制到前一个点(如果存在)的数据点。

如果数据点不存在,则绘制位置进行插值,并将其类设置为触发使线条消失的CSS,然后在给定有效数据时平滑过渡。线标记再次显示所有有效数据点,即使没有任何内容可以链接到它们。这是一个版本,我为没有数据的点添加了一个“问号”线标记,显示了它们的位置是如何插值的:
http://fiddle.jshell.net/pwmA3/4/

需要注意的一点是:为每个数据点而不是路径使用线段意味着浏览器需要跟踪更多DOM对象。如果你有数百个数据点,这可能会减慢速度,因为内插缺失点的所有额外计算也是如此。如果这成为问题,我建议使用带有svg:line选项的d3折线图方法(如第一个示例中所示),然后跳过转换。