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

dma*_*nez 5 d3.js

我画了一些有一些"空白"的折线图(路径).我在间隙之间使用了一个技巧,然后在我的数据间隙之间没有插值.

例:

pos, value
1,10
2,15
3,20
8,5
9,6
10,20
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

任何见解都受到高度赞赏.

Ame*_*aBR 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
}
Run Code Online (Sandbox Code Playgroud)

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

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

现在来图...

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

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

TA-大新! http://fiddle.jshell.net/9jTk4/2/)

魔术代码是这个链中的最后一个语句:

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
Run Code Online (Sandbox Code Playgroud)

请注意,数据点是使用SVG行标记创建的,这样即使它们没有连接任何东西也可以看到点,因为它们的邻居是未定义的.

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

问题是当您添加转换时.

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

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

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

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

TA-大新! AlaKaZAM !!! http://fiddle.jshell.net/pwmA3/3/

除了平滑过渡之外,它看起来与上一版本非常相似,但实际上它在结构上非常不同.它不使用单个路径来表示整行,而是使用一系列svg:line元素,每个元素都将路径绘制到前一个点(如果存在)的数据点.

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

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

  • @dmartinez将来,如果你想出一个问题的解决方案(即使它不是最佳的),请回来并添加一个答案.它有助于为将来发现此问题的其他人分享知识,并确保那些回答的人不会重复工作.但这个问题令我感到困惑,我很高兴我发现了`d3.svg.line().defined()`方法 - 以及它的局限性. (3认同)