减少大型数据集 DOM 个元素的策略

Strategies to reduce DOM elements of large data sets

我有一个大型数据集,我想使用 dc.js 显示它。条目数量远远超过屏幕上的可用绘图 space(以像素为单位)。所以在 500px 宽的图表上渲染 20k 点是没有意义的,而且还会减慢浏览器的速度。

我阅读了 Performance teak section of the wiki 并想到了其他一些事情:

dc.js 提供了一个简洁的 rangeChart 可用于显示我想要使用的范围选择。

但是我放大 rangeChart 越多,我想显示的细节就越多。但我不知道如何获得图表的缩放级别和聚合组 'on the fly'。也许有人对此有想法。

我创建了一个codepan作为示例。

这经常出现,所以我添加了一个 focus dynamic interval 示例。

它是 switching time intervals example, except here we determine which d3 time interval 中相同技术的改进,可根据范围图表中画笔的范围使用。

不幸的是我现在没有时间调整它,所以让我们迭代一下。 IMO 它几乎但还不够快——它可以采样更少的点,但我使用了内置的时间间隔。当您在 dc 折线图中看到一条锯齿线时

这通常是因为您显示的 太多 点 - 应该有几十个而不是数百个,永远不会有数千个。

想法是在不同的时间间隔生成不同的组。在这里,我们将定义一些间隔和阈值(以毫秒为单位),我们应该在该阈值处使用该间隔:

    var groups_by_min_interval = [
        {
            name: 'minutes',
            threshold: 60*60*1000,
            interval: d3.timeMinute
        }, {
            name: 'seconds',
            threshold: 60*1000,
            interval: d3.timeSecond
        }, {
            name: 'milliseconds',
            threshold: 0,
            interval: d3.timeMillisecond
        }
    ];

同样,这里应该有更多 - 因为我们将动态生成组并缓存它们,所以有一堆没关系。 (它可能会在某些时候占用内存,但这些天在 JS 中千兆字节是可以的。)

当我们需要一个组时,我们将使用d3 interval函数生成它,它产生floor,然后减少total和count:

    function make_group(interval) {
        return dimension.group(interval).reduce(
            function(p, v) {
                p.count++;
                p.total += v.value;
                return p;
            },
            function(p, v) {
                p.count--;
                p.total += v.value;
                return p;
            },
            function() {
                return {count: 0, total: 0};
            }
        );
    }

因此,我们将告诉图表计算其 valueAccessor 内的平均值:

    chart.valueAccessor(kv => kv.value.total / kv.value.count)

这是有趣的部分:当我们需要一个组时,我们将扫描此列表,直到找到阈值小于当前范围(以毫秒为单位)的第一个规范:

    function choose_group(extent) {
        var d = extent[1].getTime() - extent[0].getTime();
        var found = groups_by_min_interval.find(mg => mg.threshold < d);
        console.log('interval ' + d + ' is more than ' + found.threshold + ' ms; choosing ' + found.name +
                    ' for ' + found.interval.range(extent[0], extent[1]).length + ' points');
        if(!found.group)
            found.group = make_group(found.interval);
        return found.group;
    }

将此连接到范围图表的 filtered 事件:

    rangeChart.on('filtered.dynamic-interval', function(_, filter) {
        chart.group(choose_group(filter || fullDomain));
    });

运行暂时没时间了。请提出任何问题,我们会更好地完善它。我们将需要自定义时间间隔(如十分之一秒),但我现在找不到该示例。有个好办法。

注意:我给你加了一个,点数增加了一个数量级,达到五十万。这对于旧计算机来说可能太多了,但在 2017 年的计算机上它证明数据量不是问题,DOM 个元素是。