Эффективное отображение в браузере тиковых данных временных рядов? - PullRequest
0 голосов
/ 14 апреля 2020

Я создаю веб-приложение для имитации обработки некоторых реальных данных. Частично это генерирует смоделированные данные, которые я моделирую очень просто как dr aws из нормального распределения во времени. Я подумал, что было бы неплохо построить эти данные, так как они тикают в режиме реального времени вместе со скользящим средним (результат см. Ниже). Я использую реагировать и заговор , в частности реагировать заговор на достижение sh это.

data plot

Я был очень доволен результатом, пока не сложил несколько этих сюжетов на одной странице и не понял, что он начинает сильно пыхтеть. По общему признанию, я не работаю на новейших машинах, но все же, я был удивлен, что только несколько графиков на экране при 10 отрисовках в секунду c вызывали скачок ЦП выше 100%, распространяющийся по ядрам.

Итак, мой вопрос: есть ли более эффективные опции для отображения тиковых данных в браузере? Моя единственная мысль состояла в том, чтобы использовать scattergl тип графика plotly вместо scatter, но, похоже, это не имеет значения. С другой стороны, я делаю что-то явно глупое в своем текущем коде, или, возможно, есть какой-то способ, которым я мог бы оптимизировать это, чего не вижу?

Полный код ниже, сводится только к соответствующим частям и комментируется для ясность. Заранее спасибо.

import React, { useState, useEffect } from "react";
import Plot from "react-plotly.js";
import randNormal from "@stdlib/random/base/normal";
import range from "lodash.range";

export default function SimulationSettingRunningAverage({
  mean,
  std,
  decayRate,
  drawsPerSecond,
}) {
  const historyToKeep = drawsPerSecond * 3;
  const [runningAverage, setRunningAverage] = useState([mean]);
  const [drawHistory, setDrawHistory] = useState([mean]);

  // create a trigger to create a draw timer
  useEffect(() => {
    const draw = () => {
      const sample = randNormal(mean, std);
      // set draw history, slicing off the oldest value if at capacity
      setDrawHistory((dh) => [
        ...dh.slice(dh.length > historyToKeep ? 1 : 0),
        sample,
      ]);
      // set running average by decaying the next most recent value and slicing
      // off the oldest value if at capacity
      setRunningAverage((ra) => [
        ...ra.slice(ra.length > historyToKeep ? 1 : 0),
        ra.slice(-1).pop() * (1 - decayRate) + decayRate * sample,
      ]);
    };
    const id = setInterval(draw, 1000 / drawsPerSecond);
    return () => clearInterval(id);
  }, [
    mean,
    std,
    decayRate,
    drawsPerSecond,
    historyToKeep,
    setDrawHistory,
    setRunningAverage,
  ]);

  return (
    <React.Fragment>
      <Plot
        data={[
          {
            x: range(0, drawHistory.length),
            y: drawHistory,
            name: "Draws",
            type: "scattergl",
            mode: "markers",
            marker: { color: "red" },
          },
          {
            x: range(0, runningAverage.length),
            y: runningAverage,
            name: "Running average",
            type: "scattergl",
            mode: "lines",
            line: { color: "black" },
          },
        ]}
        layout={{
          title: "Latest draws",
          width: 320,
          height: 240,
          margin: { l: 50, r: 30, t: 30, b: 30 },
          xaxis: {
            range: [0, historyToKeep],
            showticklabels: false,
            zeroline: false,
            title: "Time",
          },
          yaxis: {
            range: [mean - std * 4, mean + std * 4],
            zeroline: false,
            title: "Value",
          },
          showlegend: true,
          legend: {
            x: 1,
            xanchor: "right",
            y: 1,
            bgcolor: "rgba(0, 0, 0, 0)",
          },
        }}
        config={{ staticPlot: true }}
      />
    </React.Fragment>
  );
}
...