Первая спецификация: от JSON до графика

В основе Vega и Vega-Lite лежит принцип декларативного описания визуализации через JSON-структуру. Вместо пошагового императивного кода, описывающего создание графических примитивов, используется единая спецификация, которая полностью определяет:

  • источник данных
  • тип визуализации
  • преобразования данных
  • отображение каналов (encoding)
  • параметры масштаба и осей
  • интерактивное поведение

Ключевая особенность подхода заключается в том, что спецификация является полным описанием состояния графика, а не набором инструкций для его построения.

Минимальная структура Vega-Lite выглядит следующим образом:

{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {
    "values": [
      { "category": "A", "value": 28 },
      { "category": "B", "value": 55 },
      { "category": "C", "value": 43 }
    ]
  },
  "mark": "bar",
  "encoding": {
    "x": { "field": "category", "type": "nominal" },
    "y": { "field": "value", "type": "quantitative" }
  }
}

Эта структура уже содержит полный цикл преобразования данных в визуальный объект: от массива JSON до отрисованной диаграммы.


Архитектурный принцип Vega-Lite

Vega-Lite представляет собой высокоуровневую абстракцию над Vega. Основная идея заключается в разделении ответственности:

  • Vega-Lite описывает что должно быть показано
  • Vega определяет как это должно быть отрисовано

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

Такой подход формирует многоуровневую модель:

  1. JSON-спецификация Vega-Lite
  2. Компиляция в Vega specification
  3. Исполнение Vega runtime
  4. Рендеринг через Canvas или SVG

Данные как первичный элемент модели

Любая спецификация начинается с блока данных. Vega-Lite поддерживает несколько источников:

  • встроенные массивы (values)
  • загрузка из URL (url)
  • соединение с внешними форматами (CSV, JSON, TSV)
  • потоковые обновления

Пример загрузки внешнего набора:

{
  "data": {
    "url": "data/cars.json"
  }
}

Данные в Vega-Lite считаются неизменяемыми в момент рендеринга, а любые преобразования описываются декларативно через transform.


Марки (mark) как визуальные примитивы

Поле mark определяет тип графического представления. Это фундаментальный элемент, задающий семантику визуализации.

Основные типы марок:

  • bar — столбчатые диаграммы
  • line — линейные графики
  • point — точечные диаграммы
  • area — площадные графики
  • text — текстовые аннотации
  • rect — прямоугольные области

Каждая марка задаёт базовую геометрию, которая затем параметризуется через encoding.

Пример линейного графика:

{
  "mark": "line",
  "encoding": {
    "x": { "field": "date", "type": "temporal" },
    "y": { "field": "temperature", "type": "quantitative" }
  }
}

Encoding: связывание данных и визуальных каналов

Encoding представляет собой центральный механизм трансформации данных в графику. Он определяет, какие поля данных соответствуют визуальным каналам.

Основные каналы:

  • x, y — координаты
  • color — цветовое кодирование
  • size — размер
  • shape — форма
  • opacity — прозрачность
  • tooltip — интерактивные подсказки

Пример многоканального отображения:

{
  "mark": "point",
  "encoding": {
    "x": { "field": "horsepower", "type": "quantitative" },
    "y": { "field": "mpg", "type": "quantitative" },
    "color": { "field": "origin", "type": "nominal" },
    "size": { "field": "weight", "type": "quantitative" }
  }
}

Каждый канал работает как функция отображения данных в визуальное пространство.


Типизация данных и её влияние на визуализацию

Vega-Lite использует строгую систему типов данных, которая влияет на выбор шкал и преобразований:

  • quantitative — числовые значения
  • temporal — временные ряды
  • ordinal — упорядоченные категории
  • nominal — неупорядоченные категории

Тип данных определяет поведение шкал (scales), осей (axes) и агрегирующих функций.

Например, временной тип автоматически приводит к построению временной шкалы с корректной интерполяцией дат.


Масштабы и координатные преобразования

Каждый визуальный канал проходит через функцию масштабирования. Vega-Lite автоматически создаёт шкалы, но допускает их явную настройку.

Пример ручной настройки шкалы:

{
  "encoding": {
    "y": {
      "field": "value",
      "type": "quantitative",
      "scale": { "domain": [0, 100], "nice": true }
    }
  }
}

Шкалы выполняют преобразование из домена данных в диапазон пикселей.


Преобразования данных (transform)

Блок transform позволяет модифицировать данные до рендеринга. Поддерживаются:

  • фильтрация
  • агрегация
  • сортировка
  • вычисляемые поля
  • оконные функции

Пример агрегации:

{
  "transform": [
    {
      "aggregate": [
        { "op": "mean", "field": "value", "as": "avgValue" }
      ],
      "groupby": ["category"]
    }
  ]
}

Такие операции позволяют формировать новые представления без изменения исходного набора данных.


Компиляция Vega-Lite в Vega

Процесс преобразования спецификации включает несколько этапов:

  1. Валидация JSON-схемы
  2. Разрешение абстракций (mark + encoding → primitive marks)
  3. Генерация scales, axes, legends
  4. Построение Vega runtime-структуры

Результатом является чистая Vega-спецификация, которая уже не содержит высокоуровневых сокращений.


Простейший путь от JSON к графику

Рассмотрим полный конвейер:

{
  "data": {
    "values": [
      { "x": 1, "y": 3 },
      { "x": 2, "y": 5 },
      { "x": 3, "y": 2 }
    ]
  },
  "mark": "line",
  "encoding": {
    "x": { "field": "x", "type": "quantitative" },
    "y": { "field": "y", "type": "quantitative" }
  }
}

Этот JSON проходит следующие стадии:

  • интерпретация массива данных
  • построение числовых шкал для x и y
  • генерация линии через последовательность точек
  • отрисовка на Canvas или SVG

Роль декларативного подхода

Декларативная модель Vega-Lite устраняет необходимость управления графическим состоянием напрямую. Вместо этого система оперирует:

  • описанием структуры данных
  • связями между полями и визуальными каналами
  • правилами трансформации

Это приводит к тому, что один и тот же JSON может быть детерминированно преобразован в визуализацию независимо от среды выполнения.


Встраивание в JavaScript-окружение

Vega-Lite используется как библиотека спецификаций, но рендеринг выполняется через JavaScript API:

vegaEmbed('#vis', spec);

В этом случае спецификация остаётся неизменной, а JavaScript выполняет роль хоста для визуального движка.

Связь между слоями:

  • JavaScript управляет DOM-контейнером
  • Vega runtime управляет сценой
  • Vega-Lite задаёт структуру визуализации

Композиционность спецификаций

Vega-Lite поддерживает составные визуализации:

  • facet (разбиение по категориям)
  • layer (наложение графиков)
  • concat (горизонтальное и вертикальное объединение)
  • repeat (повтор шаблонов)

Пример наложения слоёв:

{
  "layer": [
    {
      "mark": "line",
      "encoding": {
        "x": { "field": "x", "type": "quantitative" },
        "y": { "field": "y1", "type": "quantitative" }
      }
    },
    {
      "mark": "line",
      "encoding": {
        "x": { "field": "x", "type": "quantitative" },
        "y": { "field": "y2", "type": "quantitative" }
      }
    }
  ]
}

Значение JSON-модели в визуализации

Использование JSON как основного носителя описания визуализации создаёт унифицированный формат, который:

  • легко сериализуется
  • совместим с API и файлами конфигурации
  • не зависит от среды исполнения
  • может быть динамически модифицирован

Это делает Vega-Lite не просто библиотекой, а формальной системой описания графиков, основанной на строгой структуре данных и трансформаций.