Язык выражений Vega Expression Language

Язык выражений Vega Expression Language (VEL) используется для описания вычислений внутри спецификаций визуализаций Vega и Vega-Lite. Он представляет собой компактный интерпретируемый язык, встроенный в декларативную модель описания графиков, позволяющий задавать динамическое поведение визуализации без необходимости перехода к полноценному JavaScript-коду.

VEL применяется в нескольких ключевых контекстах: вычисление сигналов, преобразование данных, настройка параметров отображения, обработка событий, а также динамическое управление свойствами визуальных элементов.


Контекст выполнения выражений

Выражения Vega выполняются в строго определённых контекстах, где доступны различные переменные:

signal — глобальные и локальные сигналы, определяющие состояние визуализации datum — текущая строка данных в трансформациях и кодировании event — данные события взаимодействия (мышь, клавиатура) item — текущий графический элемент (mark item) parent — родительский элемент в иерархических структурах

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

Пример обращения к данным в выражении:

datum.value * 2

Базовая структура выражений

VEL использует JavaScript-подобный синтаксис, но с ограниченным набором операций. Поддерживаются:

  • арифметические операции: +, -, *, /, %
  • логические операции: &&, ||, !
  • сравнения: ==, !=, >, <, >=, <=
  • тернарный оператор: condition ? a : b

Пример:

datum.sales > 100 ? "high" : "low"

Выражения всегда возвращают значение и не поддерживают побочные эффекты.


Работа с сигналами

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

Доступ к сигналам осуществляется напрямую по имени:

width / 2

или в более сложных выражениях:

scaleFactor * (signalValue + offset)

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


Функции языка выражений

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

Математические функции

  • abs(x) — модуль числа
  • round(x) — округление
  • floor(x), ceil(x) — округление вниз и вверх
  • pow(x, n) — степень
  • sqrt(x) — квадратный корень
  • log(x), exp(x) — логарифм и экспонента
  • min(a, b, ...), max(a, b, ...)

Пример:

sqrt(datum.x * datum.x + datum.y * datum.y)

Строковые функции

  • substring(str, start, end)
  • indexof(str, search)
  • replace(str, pattern, replacement)
  • lower(str), upper(str)
  • trim(str)

Пример:

upper(datum.category)

Функции работы с массивами

  • length(array)
  • indexof(array, value)
  • inrange(value, array)
  • join(array, separator)

Пример:

length(datum.items)

Функции времени

VEL включает поддержку работы с датами через встроенный объект Date и набор утилит:

  • now() — текущее время
  • time(d) — преобразование в timestamp
  • year(d), month(d), date(d)
  • hours(d), minutes(d), seconds(d)

Пример:

year(datum.timestamp)

Доступ к данным (datum)

Объект datum предоставляет доступ к текущей записи данных в контексте трансформаций и визуальных кодировок.

Пример простого доступа:

datum.value

Сложные вычисления часто комбинируют несколько полей:

datum.revenue - datum.cost

или условные конструкции:

datum.value > threshold ? datum.value : 0

Обработка событий (event)

В выражениях взаимодействия доступен объект event, содержащий данные пользовательских действий:

  • координаты курсора: event.x, event.y
  • состояние клавиш: event.shiftKey, event.ctrlKey
  • тип события: event.type

Пример:

event.shiftKey ? datum.selected : false

Условные выражения и логика

Логические конструкции используются для фильтрации и управления отображением:

datum.value > 50 && datum.category == "A"

Тернарный оператор применяется для более компактных ветвлений:

datum.value > 0 ? datum.value : 0

Комбинация условий:

datum.a > 10 || (datum.b < 5 && datum.c == 0)

Типизация и преобразования

VEL не является строго типизированным языком, однако выполняет автоматические преобразования типов:

  • строки приводятся к числам при арифметике
  • числа преобразуются в строки при конкатенации
  • логические значения участвуют в числовых операциях как 0 и 1

Пример:

"Value: " + datum.value

Использование выражений в Vega спецификации

В сигналах

{
  "name": "opacity",
  "value": 0.5,
  "on": [
    {
      "events": "mouseover",
      "update": "1"
    },
    {
      "events": "mouseout",
      "update": "0.5"
    }
  ]
}

В кодировании mark

{
  "type": "circle",
  "encode": {
    "enter": {
      "x": {"field": "x"},
      "y": {"field": "y"},
      "size": {"expr": "datum.value * 10"}
    }
  }
}

В трансформациях

{
  "type": "filter",
  "expr": "datum.value > 100"
}

Приоритет операций

При вычислении выражений соблюдается стандартный порядок:

  1. скобки
  2. унарные операции (!, -)
  3. умножение и деление
  4. сложение и вычитание
  5. сравнения
  6. логические операции

Пример:

datum.a + datum.b * datum.c

эквивалентно:

datum.a + (datum.b * datum.c)

Безопасность и ограничения

Язык выражений Vega намеренно ограничен:

  • отсутствует доступ к внешнему окружению
  • нет возможности выполнять произвольный JavaScript
  • отсутствуют циклы и рекурсия
  • запрещены побочные эффекты
  • отсутствует создание новых функций

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


Примеры комплексных выражений

Фильтрация и нормализация значений:

datum.value > 0 ? sqrt(datum.value) / maxValue : 0

Интерактивное выделение:

event.item && event.item.datum.id == datum.id ? 1 : 0.3

Группировка по времени:

year(datum.date) + "-" + month(datum.date)

Комбинированная логика отображения:

datum.active && datum.score > threshold ? "visible" : "hidden"