优化一组 dc.js 折线图
Posted
技术标签:
【中文标题】优化一组 dc.js 折线图【英文标题】:Optimising a group of dc.js line graphs 【发布时间】:2015-06-18 02:50:22 【问题描述】:我有一组图表为我可视化了一堆数据 (here),它基于一个包含大约 25,000 行数据的 csv,每个数据有 12 个参数。但是,进行任何交互(例如在任何图表上使用画笔选择范围)都是缓慢且笨拙的,这与 the dc.js demo found here 完全不同,the dc.js demo found here 也处理数千条记录但保持流畅的动画,或者crossfilter's demo here 具有记录(航班)是我的 10 倍。
我知道主要的资源消耗是两个折线图,因为它们每 15 分钟就有一个数据点,持续了大约 8 个月。删除它们中的任何一个会使图表再次响应,但它们是可视化的主要特征,那么有什么方法可以让它们显示不那么细粒度的数据?
这两个折线图的具体代码如下:
var lineZoomGraph = dc.lineChart("#chart-line-zoom")
.width(1100)
.height(60)
.margins(top: 0, right: 50, bottom: 20, left: 40)
.dimension(dateDim)
.group(tempGroup)
.x(d3.time.scale().domain([minDate,maxDate]));
var tempLineGraph = dc.lineChart("#chart-line-tempPer15Min")
.width(1100).height(240)
.dimension(dateDim)
.group(tempGroup)
.mouseZoomable(true)
.rangeChart(lineZoomGraph)
.brushOn(false)
.x(d3.time.scale().domain([minDate,maxDate]));
单独但相关的问题;如何修改折线图上的 y 轴?默认情况下,它们不包含数据集中找到的最高值和最低值,这看起来很奇怪。
编辑:我写的一些代码试图解决这个问题:
var graphWidth = 1100;
var dataPerPixel = data.length / graphWidth;
var tempGroup = dateDim.group().reduceSum(function(d)
if (d.pointNumber % Math.ceil(dataPerPixel) === 0)
return d.warmth;
);
d.pointNumber 是每个数据点的唯一点 ID,从 0 到 22,000 ish 累积。然而,现在折线图显示为空白。我使用 tempGroup.all() 检查了该组的数据,现在每个第 21 个数据点都有一个温度值,但所有其他数据点都有 NaN。我根本没有成功减少小组规模;它仍然在22000左右。我想知道这是否是正确的方法......
编辑 2:找到了不同的方法。我通常创建 tempGroup,但随后创建另一个组,该组对现有 tempGroup 进行更多过滤。
var tempGroup = dateDim.group().reduceSum(function(d) return d.warmth; );
var filteredTempGroup =
all: function ()
return tempGroup.top(Infinity).filter( function (d)
if (d.pointNumber % Math.ceil(dataPerPixel) === 0) return d.value;
);
;
我在这里遇到的问题是 d.pointNumber 不可访问,所以我无法判断它是否是第 N 个数据点(或它的倍数)。如果我将它分配给一个 var 无论如何它只是一个固定值,所以我不知道如何解决这个问题......
【问题讨论】:
第二次编辑中的“假组”方法似乎是合理的。由于您的数据可能无论如何都按日期顺序排列(?),索引应该与pointNumber
几乎相同,因此向过滤器回调函数添加一个参数应该会给您一个可以使用的索引:.filter( function (d, i) return (i % Math.ceil(dataPerPixel) === 0); )
。另请注意,过滤器回调函数应返回布尔值而不是值。
好的,这样就可以了。我得到了一个更易于管理的 1071 结果,但结果也乱七八糟,这让我很困惑。如果您现在查看实时网站,您就会明白我的意思。该组的对象从前几个数据点开始正确,然后向前跳几天,然后又跳回来……所以这些点很好,只是有点混乱。
嗯,是的,您可能想使用.all()
而不是.top(Infinity)
,原因很明显。错过了。
成功了,太好了:) 这背后的原因到底是什么?此外,我希望分辨率更改以适应缩放级别,因为现在当我放大时没有足够的点并且看起来有点块状......有没有办法知道当前显示了多少数据点任何时间/缩放级别的图表?
.all()
按键排序,.top()
按值排序。您自己正在定义 dc.js 看到的数据点的数量,但您可以使用 chart.x().range()
和每单位时间的观察数量来确定有多少数据点可供采样。
【参考方案1】:
在处理基于 d3 的图表的性能问题时,通常的罪魁祸首是 DOM 元素的数量,而不是数据的大小。请注意,crossfilter 演示有很多行数据,但只有几百条。
看起来您可能正在尝试绘制所有点而不是聚合它们。我想由于您正在做一个时间序列,聚合点可能不直观,但考虑到您的绘图只能显示 1100 个点(宽度),因此过度使用 SVG 引擎绘制 25,000 个点是没有意义的。
我建议将其降低到 100-1000 个垃圾箱之间,例如通过平均每天:
var daysDim = data.dimension(function(d) return d3.time.day(d.time); );
function reduceAddAvg(attr)
return function(p,v)
if (_.isLegitNumber(v[attr]))
++p.count
p.sums += v[attr];
p.averages = (p.count === 0) ? 0 : p.sums/p.count; // gaurd against dividing by zero
return p;
;
function reduceRemoveAvg(attr)
return function(p,v)
if (_.isLegitNumber(v[attr]))
--p.count
p.sums -= v[attr];
p.averages = (p.count === 0) ? 0 : p.sums/p.count;
return p;
;
function reduceInitAvg()
return count:0, sums:0, averages:0;
...
// average a parameter (column) named "param"
var daysGroup = dim.group().reduce(reduceAddAvg('param'), reduceRemoveAvg('param'), reduceInitAvg);
(可重复使用的平均reduce函数from the FAQ)
然后指定您的xUnits
进行匹配,并使用elasticY
自动计算y 轴:
chart.xUnits(d3.time.days)
.elasticY(true)
【讨论】:
感谢您的建议...我在 d3 中使用折线图上的重采样函数寻找其他人,但那里没有具体的例子,我觉得这很奇怪,因为一定有人遇到了我的问题在某些时候......我尝试使用我在原始答案中进行编辑的函数自己解决它,但问题是它并没有摆脱原始数据,只会使每个 N 数据点有一个值,其余的是 NaN。 你使用了 Gordon 建议的 reduce 函数吗? 抽样实际上是一种数据技术,而不是一种图表技术,如果这有助于您进行谷歌搜索的话。你的尝试对我来说看起来基本没问题,但我还没有尝试过。就个人而言,我会坚持使用平均值以免丢失数据,因为在 javascript 中这个数据量应该没问题以上是关于优化一组 dc.js 折线图的主要内容,如果未能解决你的问题,请参考以下文章