Потоковые данные

В этом уроке вы научитесь передавать новые данные в визуализацию Vega-Lite в потоковом режиме. Если вы пока не знакомы с Vega-Lite, сначала прочитайте вводный урок.

Мы будем использовать API View из Vega и обновлять данные методом change. Данные в Vega и Vega-Lite обновляются либо через взаимодействие с визуализацией, либо через явно заданные изменения. В обоих случаях данные проходят через среду выполнения Vega, которая оптимизирует обработку. Этот урок посвящён второму варианту: потоковой передаче данных без действий пользователя.

Чтобы использовать view.change, сначала укажите имя обновляемого источника данных: view.change('data', ... ). Это имя нужно задать в исходной спецификации, как описано в разделе именованных источников. Второй аргумент view.change определяет, какие данные добавить и какие удалить. Для добавления передайте массив записей с той же структурой, что у существующих данных. В нашем примере она выглядит так:

{
  "x": number,
  "y": number,
  "category": number
}

Для удаления точек можно передать сами записи, аналогично добавлению выше (для этого вам нужно хранить эти записи), либо отобрать существующие данные по значениям полей и передать предикат в remove(). Здесь используется function (t) { return t.x < minimumX; }. Если вы знакомы с D3, эта схема insert и remove напоминает подход enter/exit.

Объединим эти операции:

var changeSet = vega
  .changeset()
  .insert(valueGenerator())
  .remove(function (t) {
    return t.x < minimumX;
  });
view.change('table', changeSet).run();

Объект представления предоставляет метод встраивания, который помогает загружать спецификации Vega/Vega-Lite на веб-страницы. При успешном завершении он возвращает выполненный Promise с объектом, содержащим view и spec. Через view можно обратиться к описанному выше view.change, а spec содержит спецификацию Vega, скомпилированную из исходной спецификации Vega-Lite. При ошибке встраивания возвращается отклонённый Promise с объектом ошибки.

Для простоты мы генерируем данные, имитируя обновления в реальном времени. Этот код можно легко заменить запросом данных с сервера. В примере «реальное время» имитирует window.setInterval, периодически вызывающий view.change.

Ниже приведён JavaScript-код примера. Убедитесь, что ваша HTML-страница содержит элемент div с идентификатором 'chart'.

var vlSpec = {
  $schema: 'https://vega.github.io/schema/vega-lite/v6.json',
  data: {name: 'table'},
  width: 400,
  mark: 'line',
  encoding: {
    x: {field: 'x', type: 'quantitative', scale: {zero: false}},
    y: {field: 'y', type: 'quantitative'},
    color: {field: 'category', type: 'nominal'},
  },
};
vegaEmbed('#chart', vlSpec).then(function (res) {
  /**
   * Создаёт новые записи с помощью случайного блуждания.
   */
  function newGenerator() {
    var counter = -1;
    var previousY = [5, 5, 5, 5];
    return function () {
      counter++;
      var newVals = previousY.map(function (v, c) {
        return {
          x: counter,
          y: v + Math.round(Math.random() * 10 - c * 3),
          category: c,
        };
      });
      previousY = newVals.map(function (v) {
        return v.y;
      });
      return newVals;
    };
  }

  var valueGenerator = newGenerator();
  var minimumX = -100;
  window.setInterval(function () {
    minimumX++;
    var changeSet = vega
      .changeset()
      .insert(valueGenerator())
      .remove(function (t) {
        return t.x < minimumX;
      });
    res.view.change('table', changeSet).run();
  }, 1000);
});

Новые данные могут менять компоновку, но Vega не всегда пересчитывает размеры диаграммы. Из-за этой оптимизации, например, оси могут оказаться обрезанными. Чтобы решить проблему, настройте autosize или явно вызывайте view.resize при обновлении данных.

На этом урок заканчивается: вы научились передавать новые данные в диаграмму в потоковом режиме. Для использования Vega-Lite с WebSocket в оригинале приведён демонстрационный проект «Vega-Lite with websockets». Другие визуализации можно найти в галерее, а дополнительные настройки — в документации.