Сигналы в Vega представляют собой реактивные переменные, лежащие в основе всей системы исполнения спецификаций. Они формируют динамический слой поверх данных, трансформаций и отрисовки, позволяя связывать пользовательские взаимодействия, вычисления и визуальные свойства в единую реактивную сеть.
Каждый сигнал — это именованное значение, которое может:
Ключевая особенность заключается в том, что сигналы не принадлежат строго ни к данным, ни к визуальному слою. Они существуют как промежуточный реактивный механизм, обеспечивающий связь между этапами dataflow-пайплайна Vega.
Базовая форма сигнала:
{
"signals": [
{
"name": "threshold",
"value": 10
}
]
}
Сигнал может быть вычисляемым:
{
"name": "dynamicThreshold",
"update": "width / 2"
}
Или зависеть от событий:
{
"name": "mouseX",
"on": [
{
"events": "mousemove",
"update": "x()"
}
]
}
Эта модель делает сигналы универсальным механизмом управления поведением всей визуализации.
Сигналы в transform используются для динамического изменения данных на этапе обработки. Transform в Vega — это последовательность операций над данными (filter, calculate, lookup, aggregate и другие), и сигналы могут участвовать в этих операциях через выражения.
Наиболее типичный случай — фильтрация данных по сигналу:
{
"type": "filter",
"expr": "datum.value > threshold"
}
Здесь threshold — сигнал, определённый на верхнем уровне
спецификации. При изменении сигнала пересчитывается результат transform,
и данные автоматически обновляются.
Transform formula (calculate) позволяет создавать новые
поля, зависящие от сигналов:
{
"type": "formula",
"as": "scaledValue",
"expr": "datum.value * scaleFactor"
}
Где scaleFactor — сигнал.
Такая конструкция делает трансформации параметризуемыми без необходимости пересобирать спецификацию.
Сигналы часто используются для переключения логики внутри transform:
{
"type": "formula",
"as": "category",
"expr": "useLogScale ? log(datum.value) : datum.value"
}
Здесь один сигнал управляет всей стратегией обработки данных.
Сигналы могут влиять на группировки и агрегирование через выражения, подаваемые в calculate перед aggregate:
{
"type": "formula",
"as": "bucket",
"expr": "floor(datum.value / binSize)"
}
Где binSize управляется сигналом, позволяя динамически
менять дискретизацию без изменения структуры transform-пайплайна.
Encode — это слой, отвечающий за визуальные свойства mark-ов: позицию, цвет, размер, форму и другие атрибуты. Именно здесь сигналы проявляют себя наиболее явно, поскольку напрямую управляют тем, как визуализация выглядит в каждый момент времени.
В Vega encode любое поле может быть задано через сигнал:
{
"type": "rect",
"encode": {
"enter": {
"width": { "signal": "barWidth" },
"fill": { "value": "steelblue" }
}
}
}
Сигнал barWidth может динамически меняться, влияя на
отрисовку всех элементов.
Сигналы часто используются для управления геометрией:
{
"encode": {
"update": {
"x": { "signal": "xScale(datum.category)" },
"y": { "signal": "yScale(datum.value)" }
}
}
}
Здесь сигнал используется не только как число, но и как выражение, включающее функции шкал.
Encode позволяет привязывать визуальные стили к сигналам состояния интерфейса:
{
"fill": {
"signal": "datum.id === selectedId ? 'orange' : 'gray'"
}
}
Такой подход делает визуальные состояния полностью реактивными.
Encode тесно связан с событиями, и сигналы могут изменяться прямо из пользовательских действий:
{
"hover": {
"fillOpacity": { "value": 1 }
},
"update": {
"fillOpacity": { "signal": "hovered ? 1 : 0.6" }
}
}
Сигнал hovered может управляться через event stream, а
encode автоматически реагирует на его изменение.
Наиболее важная концепция заключается в том, что сигналы формируют непрерывную связь между transform и encode, создавая реактивный поток данных:
Пример комплексной цепочки:
{
"signals": [
{ "name": "binSize", "value": 10 }
],
"data": [
{
"name": "table",
"transform": [
{
"type": "formula",
"as": "binned",
"expr": "floor(datum.value / binSize) * binSize"
}
]
}
],
"marks": [
{
"type": "rect",
"encode": {
"update": {
"x": { "signal": "xScale(datum.binned)" },
"width": { "signal": "xScale.bandwidth()" }
}
}
}
]
}
Изменение binSize приводит к полной перестройке данных и
мгновенному обновлению визуального представления.
Особое значение имеют выражения Vega Expression Language, где сигналы доступны как глобальные переменные. Это позволяет строить вычисления, не ограниченные структурой transform.
{
"type": "filter",
"expr": "datum.value > minValue && datum.value < maxValue"
}
Оба сигнала управляют диапазоном фильтрации.
В более сложных случаях сигналы формируют параметры моделей:
{
"type": "formula",
"as": "normalized",
"expr": "(datum.value - meanValue) / stdDev"
}
Такая конструкция делает transform зависимым от статистических параметров, вычисляемых или задаваемых динамически.
В архитектуре Vega сигналы часто выступают как централизованное состояние визуализации. Transform формирует данные, encode визуализирует их, а сигналы связывают оба уровня через единый реактивный контекст.
Особенно важно, что:
Сигналы позволяют изменять структуру transform без изменения самой спецификации. Например, можно управлять включением фильтра:
{
"type": "filter",
"expr": "enableFilter ? datum.value > threshold : true"
}
Или переключать режим агрегации:
{
"type": "aggregate",
"ops": { "signal": "aggType" }
}
Хотя не все поля transform напрямую поддерживают сигналы, выражения позволяют обойти это ограничение через вычисления.
Encode является конечным этапом реактивного графа Vega, где сигналы определяют итоговое визуальное состояние. Любое изменение сигнала проходит следующий путь:
В результате encode становится не статической декларацией, а реактивным отображением состояния сигналов.
Сложные визуализации часто используют сигналы как композиционные элементы:
{
"signals": [
{ "name": "scaleFactor", "value": 1 },
{ "name": "offset", "value": 0 }
]
}
В transform:
{
"type": "formula",
"as": "computed",
"expr": "datum.value * scaleFactor + offset"
}
В encode:
{
"y": { "signal": "yScale(datum.computed)" }
}
Такой подход обеспечивает единое пространство параметров для всей визуализации, где transform и encode работают как части одного реактивного контура.