提高大量数据的highcharts性能

问题描述 投票:10回答:7

我想获得更多的数据。示例数据如下

1850/01   -0.845   -0.922   -0.748   -1.038   -0.652   -1.379   -0.311   -1.053   -0.636   -1.418   -0.272
1850/02   -0.043   -0.113    0.047   -0.244    0.159   -0.613    0.528   -0.260    0.177   -0.653    0.569
1850/03   -0.698   -0.794   -0.633   -0.891   -0.506   -1.123   -0.274   -0.910   -0.495   -1.174   -0.229
……….
2016/12    0.795    0.746    0.828    0.756    0.834    0.586    1.005    0.731    0.848    0.575    1.010
2017/01    1.025    0.977    1.067    0.983    1.068    0.786    1.265    0.963    1.084    0.778    1.271
2017/02    1.151    1.098    1.198    1.112    1.191    0.957    1.346    1.089    1.208    0.946    1.352

从1850年到2017年以及每月开始。我正在处理这个数据集,以便在像这样的Highcharts中使用它

$.each(lines, function(index, row) {
  var cells = row.split(','),
  series = {
    type: 'line',
    data:[]
  };

  $.each(cells, function(itemNo, item) {
    if (itemNo == 0) {
      series.name = item;
    } else {
      series.data.push(parseFloat(item));
    }
  });

  data.push(series);
});

我以下面的方式使用它

chart = $('#container').highcharts({
  chart: {
    polar: true
  },
  series: data
});

这确实有效,但它真的很慢。如何改进/增强其性能,以便我可以快速加载highcharts而不冻结浏览器?

更新这是我的xAxis

xAxis: {
        tickInterval: 1,
        min: 0,
        max: 12,
        categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    },

更新2

yAxis: {
        min: -1,
        max: 2.2,
        endOnTick: false,
        title: {
            text: 'Temperature (°C)'
        },
        plotLines: [{
            value: 2,
            width: 1,
            label: {
                text: '2°C',
                align: 'right',
                y: 2
            },
            color: 'red'
        }, {
            value: 1.5,
            width: 1,
            label: {
                text: '1.5°C',
                align: 'right',
                y: 30
            },
            color: 'red'
        }],
    },
javascript jquery highcharts
7个回答
7
投票

我认为这个问题需要结合其他人提出的解决方案。这些包括:

  1. 压缩数据:如果我理解正确,那么您有167年的数据和每年12个月。这些中的每一个都是一系列,总共> 2000系列。我不确定这会创建一个可解释的图表,但我也可能误解了您的数据的性质以及您计划如何绘制它。
  2. 使用Highcharts的boost.js模块:Highcharts通常将其图形呈现为SVG。我对boost.js模块的理解是,它会导致图表的某些部分在HTML5画布元素上呈现。对于大量数据点,HTML5画布比SVG快得多。在这里看一个实证比较:SVG vs canvas: how to choose
  3. 设置图表选项以最小化资源需求:我认为您遇到的减速不太可能是由于您的数据处理。相反,我认为这几乎肯定是由于Highcharts所需的渲染时间以及监视所有图表元素上的事件所需的浏览器资源。默认情况下,例如,Highcharts图允许您将鼠标“悬停”在数据点上以突出显示它们,并且它们显示工具提示,其中包含有关数据点的信息。如果您有一个包含数千个数据点的图,那么这需要您的浏览器在图表对象上处理数千个鼠标事件。关闭此图表功能应该可以提高性能。在下面的演示中,我使用鼠标关闭了工具提示和数据点突出显示。我也关闭了传奇,以提高图表的可见性。
  4. 您可以按块处理和更新数据:从长远来看,这实际上比您一次性渲染块需要更多的时间,因为Highcharts每次添加新系列时都必须重绘图表。但是,它可能会带来更好的用户体验,因为页面会显得更具响应性。下面的演示使用了这种方法。它允许您设置每个块处理的数据行数(linesPerChunk)以及一个块完成处理和您希望开始处理下一个块(timeBetweenChunks)之间的时间延迟。理想情况下,timeBetweenChunks将设置为Highcharts渲染和显示最后一个块的时间,以便计算机在处理数据和渲染数据之间交替,两者之间没有非生产性差距。理想情况下,可以自适应地设置它,以便它在计算机/用户/浏览器等之间是最佳的,但我不知道如何做到这一点;任何想法都会受到欢迎。所以目前它只是设置为一个恒定的100毫秒。使用2000行数据,每行数据100行,以及块之间100毫秒,整个过程需要大约2秒才能加载。关键功能是plotMoreData()。在处理了一个块并将新系列添加到图表之后,它使用timeBetweenChunkswindow.setTimeout(plotMoreData, timeBetweenChunks)的延迟调用自身。然后它重绘图表。当下次调用plotMoreData时,它会处理下一个块,依此类推。它在处理并显示所有数据时停止,并且还更新状态消息。

编辑:似乎Highcharts boost模块不适用于极坐标图,这是一个已知问题。这里描述了一个修复:Polar Scatter Using Boost Module。我能够通过修改boost.src.js(从the Highcharts Github repository构建如下:

在〜第1380行,我换了:

if (!settings.useGPUTranslations) {
    inst.skipTranslation = true;
    x = xAxis.toPixels(x, true);
    y = yAxis.toPixels(y, true);
}

有:

if (!settings.useGPUTranslations) {
    inst.skipTranslation = true;
    // Default (non-Polar) calculation
    if( !series.chart.polar ) {
        x = xAxis.toPixels(x, true);
        y = yAxis.toPixels(y, true);
    }
    // Handle Polar chart coordinate conversion
    else {
        var polarPoint = {
            plotX: xAxis.translate( x, 0, 0, 0, 1, series.options.pointPlacement, series.type === 'flags' ),
            plotY: yAxis.translate( y, 0, 1, 0, 1 )
        };

        series.toXY( polarPoint );
        x = Math.round( polarPoint.plotX );
        y = Math.round( polarPoint.plotY );
    }
}

这似乎有效。请参阅此处的演示:JSFiddle Polar Highcharts Boost Demo


8
投票

鉴于您显示的数据不是每月更新一次,我觉得为每个视图生成图表对您的客户来说是一个很大的资源浪费。

实际上,您可以很容易地生成图表,而无需更改您现在正在执行的任何操作,而是提取生成的SVG并将其简单地提供给访问者。

为此,您只需使用HighCharts的getSVG方法,它将以字符串形式返回SVG。

我真的不知道你是否想要某种程序来自动更新图表,但你可以使用cron作业来达到这个目的。请记住,即使使用最初的更新数据方法,您也必须做一些事情。


关于你的脚本,首先要注意的是你使用的是$.each,与性能相比,这在性能方面相当糟糕。正如这个jsPerf所证明的那样,我得到3,649 Op/s$.each,而循环可以让你达到202,755 Op/s这是疯狂的更快!既然你也做了一个双循环,差异将是^2

但同样,由于数据不经常更新,脚本的这一部分可以完全删除并转换为与函数输出相对应的JSON,HighCharts可以直接加载,跳过CSV的整个处理和转换。


如果不要求使用HighCharts,则可以使用react-vis,它是围绕数据可视化的React组件的集合。它是作为SVG顶部的api构建的。

我做了一个演示,你可以使用与你相同的数据checkout on CodePen绘制自1850年以来的月度气温。

const {
  HorizontalGridLines,
  XAxis,
  XYPlot,
  YAxis,
  LineMarkSeries,
} = reactVis

const color = d3.scaleLinear()
  .domain([1850, 2017])
  .range(['yellow', 'red'])

const Chart = () => (
  <XYPlot width={window.innerWidth - 100} height={window.innerHeight - 100}>
    <XAxis title='Month' />
    <YAxis title='Temperature' />
    <HorizontalGridLines />
    {Object.keys(data).map(key => (
      <LineMarkSeries color={color(key)} data={data[key]} key={key} />
    ))}
  </XYPlot>
)

ReactDOM.render(<Chart />, document.querySelector('#root'))

我也使用scaleLineard3方法来查看多年以来的变化,因为我认为这将是一个有趣的信息,但它可以根据您的需要进行更改。

plot

它正在使用SVG,但我们也在努力通过deck.gl的媒介与webgl集成,这将允许更多的优化获得,仍然没有准备好,我不确定你真的需要那么多,但值得注意。

免责声明:我目前在优步工作,制作了deck.gl和react-vis。


1
投票

new Array(length)

指定数组的长度而不是创建空数组。写入数组中的现有偏移量比每次编写项目时创建新偏移量要快得多。

var data = new Array(lines.length);  // Specify array length

$.each(lines, function(index, row) {
  var cells = row.split(','),
  series = {
    type: 'line',
    data: new Array(cells.length)  // Specify array length
  };

  $.each(cells, function(itemNo, item) {
    if (itemNo == 0) {
      series.name = item;
    } else {
      series.data.push(parseFloat(item));
    }
  });

  data.push(series);
});

0
投票

我会用underscore.js

 var result = _.map(lines, function (row) {
    return { type: 'line', series: _.map(row.data, function (item, index) {
      if (index >= 1)
        return parseFloat(item);
    }), name: row.data[0]}
  });

(我是用飞机上的手机写的,所以如果有任何错别字或我遗失的东西,我会道歉。只是想帮忙!)


0
投票

您没有显示您对xAxis的选择,但是如果在服务器端按摩数据不是一个选项,我建议:

  1. 首先调用加载数据。
  2. 显示在旋转轮上显示图表的位置
  3. 开始解析数据集但不要一次性加载它,而是批量加载并在解析文件的其余部分时重绘图表。

这应该避免冻结并给出数据加载的可视指示。

编辑:对评论的回应我认为不再是加载数据的问题,而是如何以有意义的方式显示数据。即使它在2秒内加载,结果也会有数千条线重叠并且完全不可读。

您应该默认几个手工选择的值(当前月份和当月以及1年,5年,10年前),然后允许用户将选择更改为合理的年/月(例如,比较1980年,2010年的2010年)然后用(chart.series [0] .update(....)绘制更新图表的那些值。数据集中没有列,所以我假设它是每月一列,但我现在看到它们似乎每行一个,11列代表一些其他数据。

我不认为极地图表的平均值或汇总数据是最好的方法,但允许比较特定的年份和月份与确切的数据,无论这些值是什么:)


0
投票

情况:

我认为最好和更合适的解决方案是处理和提供data块,它会同步图表上点的显示,并避免在文档加载时加载整个data,即使你的数据不像你那么大在你的帖子标题中描述它。

但在阅读了您的问题,您的评论和其他帖子之后,我可以看到您从一个文件中获取所有数据,并且您没有在服务器端处理数据,因此服务器端块不适合您。

替代方案:

好吧,如果您无法在服务器端处理数据,为什么不在客户端处理它,HighCharts有两个选项:

这两个演示展示了如何按范围获取数据并限制图表仅显示相关的范围数据点,这将改善时间加载性能。


0
投票

我的团队遇到了类似的问题,但在我们的例子中,我们有3个嵌套循环,当它处理数据时,它阻塞了主线程,导致糟糕的用户体验。所以我通过推迟处理数据来解决这个问题。它完成了不阻塞主线程所需的工作,并且处理速度更快。

你也可以使用由highchart开发的boost模块(https://www.highcharts.com/blog/news/175-highcharts-performance-boost/

© www.soinside.com 2019 - 2024. All rights reserved.