Условные выражения: if / ternary

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

В отличие от классического JavaScript, условная логика в Vega/Vega-Lite реализуется через собственный expression language и декларативные конструкции, что требует строгого понимания контекста выполнения и допустимых операторов.


Два уровня условной логики

Условные выражения в Vega/Vega-Lite встречаются в двух основных формах:

  1. Expression language (Vega expressions) Используется в полях signal, calculate, transform, encode.update, mark, и др.

  2. Декларативные условия (Vega-Lite condition) Используются в высокоуровневой спецификации Vega-Lite, чаще всего в encoding.

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


Тернарный оператор ? : в Vega expressions

Тернарный оператор является базовым механизмом условной логики в expression language Vega.

Общая форма

condition ? valueIfTrue : valueIfFalse

Пример: цвет точки по значению

{
  "type": "circle",
  "encode": {
    "update": {
      "fill": {
        "signal": "datum.value > 100 ? 'red' : 'steelblue'"
      }
    }
  }
}

В этом примере:

  • при datum.value > 100 цвет становится красным
  • иначе используется синий оттенок

Вложенные тернарные выражения

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

Пример многоступенчатой логики

"fill": {
  "signal": "datum.value > 200 ? 'red' : datum.value > 100 ? 'orange' : 'green'"
}

Логика интерпретируется слева направо:

  • 200 → red

  • 100 → orange

  • иначе → green

Функция if() в Vega expressions

Помимо тернарного оператора, Vega предоставляет функциональную форму:

if(test, valueIfTrue, valueIfFalse)

Пример использования

{
  "type": "text",
  "encode": {
    "update": {
      "text": {
        "signal": "if(datum.value > 50, 'High', 'Low')"
      }
    }
  }
}

Сравнение с тернарным оператором

Подход Синтаксис Особенности
ternary a ? b : c компактный, но хуже читается при вложенности
if() if(a, b, c) более явная структура, удобен в сложных выражениях

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


Условная логика в Vega-Lite: condition

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

Базовая форма

"color": {
  "condition": {
    "test": "datum.value > 100",
    "value": "red"
  },
  "value": "blue"
}

Здесь:

  • test определяет условие
  • value внутри condition — результат при истинности
  • внешний value — значение по умолчанию

Множественные условия (condition array)

Vega-Lite поддерживает массив условий, позволяя реализовать цепочку проверок.

Пример

"color": {
  "condition": [
    {
      "test": "datum.value > 200",
      "value": "red"
    },
    {
      "test": "datum.value > 100",
      "value": "orange"
    }
  ],
  "value": "green"
}

Логика выполняется последовательно: первое совпавшее условие применяется.


Условные выражения в encoding-полях

Условия применимы ко всем основным каналам визуализации:

  • color
  • size
  • opacity
  • shape
  • tooltip
  • text

Пример изменения прозрачности

"opacity": {
  "condition": {
    "test": "datum.category === 'A'",
    "value": 1
  },
  "value": 0.3
}

Использование signals и условий

В Vega signals позволяют создавать реактивные состояния, которые затем используются в условных выражениях.

Пример сигнала

"signals": [
  {
    "name": "threshold",
    "value": 100
  }
]

Использование в условии

"fill": {
  "signal": "datum.value > threshold ? 'red' : 'blue'"
}

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


Логические операторы внутри условий

Условия могут комбинироваться с логическими операторами:

  • && — логическое И
  • || — логическое ИЛИ
  • ! — отрицание

Пример сложного условия

"signal": "datum.value > 50 && datum.value < 150 ? 'orange' : 'gray'"

Встроенные функции в условиях

Expression language Vega поддерживает встроенные функции, которые часто используются внутри условных конструкций:

  • isValid(value)
  • inrange(value, min, max)
  • length(array)
  • time() и функции работы с датами

Пример с диапазоном

"fill": {
  "signal": "inrange(datum.value, 50, 100) ? 'yellow' : 'blue'"
}

Условные выражения в трансформациях

Условия применяются не только в визуальных свойствах, но и в вычислениях данных через calculate.

Пример вычисляемого поля

"transform": [
  {
    "calculate": "datum.value > 100 ? 'high' : 'low'",
    "as": "category"
  }
]

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


Различия подходов Vega и Vega-Lite

Возможность Vega Vega-Lite
ternary да через expressions
if() да через expressions
condition ограниченно основной способ
signals полный контроль ограниченный доступ
декларативные условия нет да

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


Типовые ошибки при использовании условий

1. Чрезмерная вложенность тернарных операторов

Снижает читаемость и усложняет отладку.

2. Несовместимость типов

Возврат строк и чисел в одном условии может приводить к неожиданному поведению.

3. Использование JS-синтаксиса вне expression context

Vega expressions не поддерживают полноценный JavaScript, только ограниченный набор операторов.

4. Игнорирование порядка condition array

В Vega-Lite порядок условий критичен: первое совпадение имеет приоритет.


Практическая модель выбора подхода

  • Простое бинарное ветвление → condition
  • Вычисление внутри mark/encode → ternary
  • Сложная логика → if()
  • Множественные пороги → condition array
  • Реактивное состояние → signals + expressions