【问题标题】:Improve highcharts performance for large amounts of data提高海量数据的 highcharts 性能
【发布时间】:2017-05-02 20:17:29
【问题描述】:

我正在尝试获取更多数据。示例数据如下

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'
        }],
    },

【问题讨论】:

  • 已经尝试过,但无济于事:-(.
  • 可以分块发货吗?也许是一个小型节点服务器来预先格式化它。
  • 其实就是一个文件里的全部数据。我从文件中获取并处理它,以便 Highcharts 可以使用它。
  • 知道性能问题是在 highchart 还是在您的 js 中?预处理文件不是一个选项吗? (服务器端或手动)

标签: javascript jquery highcharts


【解决方案1】:

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

  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()。在处理一个块并将新系列添加到图表后,它使用window.setTimeout(plotMoreData, timeBetweenChunks)timeBetweenChunks 的延迟调用自身。然后重新绘制图表。当下次调用plotMoreData 时,它会处理下一个块,依此类推。它在处理并显示所有数据并更新状态消息时停止。

编辑: 似乎 Highcharts 增强模块不适用于极坐标图,这是一个已知问题。此处描述了一个修复程序: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

【讨论】:

  • 感谢您提供如此详细的答案。问题是,当我将 boost 模块与极坐标图一起使用时(此解决方案非常适合柱形图),那么数据将显示为点而不是线。检查我对yAxis 属性的更新。
  • @2619,请参阅我编辑的答案,了解 Boost 不适用于 Polar 图表这一事实的可能解决方法。如果您不想在 Highcharts 上乱搞,那么最好的选择是:1) 等待 Highcharts 开发人员自己解决这个问题,或者 2) 转移到专门用于画布的不同图表包。我知道有几个,虽然我没有任何具体的经验。
【解决方案2】:

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

确实,您可以很容易地生成图表,而无需更改您现在正在做的任何事情,然后提取生成的 SVG 并将其简单地提供给您的访问者。

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

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


关于您的脚本,首先要注意的是您使用的是$.each,与普通版本相比,这在性能方面相当糟糕。正如jsPerf 所证明的那样,我得到3,649 Op/s$.each 而for 循环可以让你达到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'))

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

它使用 SVG,但我们也在努力通过deck.gl 的中介与 webgl 集成,这将带来更多的优化收益,但还没有准备好,我不确定你是否真的需要那么多,但是值得注意。

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

【讨论】:

  • 反应不是一个选项 :-(.
  • 你总是可以创建一个小组件来渲染你的图表。我也可以只使用 d3 创建一个替代方案?
  • 再一次,Highcharts 是这里唯一需要用到的库 :-(.
  • 明白!我更新了我的答案以反映您可以研究的更多方法! ;)
【解决方案3】:

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);
});

【讨论】:

    【解决方案4】:

    我会使用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]}
      });
    

    (我是在飞机上用手机写的,如果有任何错别字或遗漏的东西,我深表歉意。只是想帮忙!)

    【讨论】:

    • 这意味着我将在 highcharts 选项中使用result 作为data 属性。这意味着首先处理整个数据,然后呈现高图。这不会提高性能。
    • _.map 是嵌套 forEaches 的性能提升,但我明白你在说什么。为什么不使用 1000 条左右的记录块来执行此操作?获取前 1000 个并显示结果,然后创建一个新变量并调用以启动下一个。一旦新变量没有未定义,您可以将它与当前集合并重新分配,然后重复直到没有更多记录为止
    【解决方案5】:

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

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

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

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

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

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

    【讨论】:

    • 第 3 点正是我的想法,但是我尝试过,但我不确定如何继续使用这种方法。任何来自上面的真实示例或代码修改都将受到高度赞赏。
    • 您的 xAxis 是什么?你能创建一个 jsFiddle 吗?
    【解决方案6】:

    情况:

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

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

    替代方案:

    如果您不能在服务器端处理您的数据,为什么不在客户端处理它,HighCharts 为您提供两种选择:

    这两个 Demo 都展示了如何按范围获取数据并将图表限制为仅显示相关范围数据点,这将提高时间加载性能。

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-07-03
        • 1970-01-01
        • 1970-01-01
        • 2014-11-21
        • 1970-01-01
        • 1970-01-01
        • 2023-03-08
        • 1970-01-01
        相关资源
        最近更新 更多