我绘制了一些有一些“间隙”的折线图(路径)。我在间隙之间使用了一个技巧,然后在我的数据间隙之间没有插值。
示例:
pos, value
1,10
2,15
3,20
8,5
9,6
10,20
正如你所看到的,我在第3和第8位之间有一个间隙。这里我要做的是创建一个值为0的位置4,以及一个值为0的位置7.然后,每当我绘制我的折线图时,我得到的我的差距是正确的图表。
现在,我需要在此折线图上进行转换。鉴于我有这种“差距黑客”,我的过渡并不是美好的:( 它以一种我不喜欢的方式移动线条。
我该怎么办? 在这种情况下是否可以进行转换而不需要在我的间隙之间有很多0值?
我不想为我不需要表示的位置添加0值,否则我的数据库会增加很多,我的可视化会很慢。
非常感谢任何见解。
答案 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折线图方法(如第一个示例中所示),然后跳过转换。