В Vega и Vega-Lite трансформация calculate используется
для создания новых полей на основе выражений. Это один из ключевых
инструментов подготовки данных внутри спецификации визуализации,
позволяющий выполнять вычисления, преобразования типов, работу со
строками, условную логику и формирование производных признаков без
предварительной обработки данных на стороне JavaScript.
В Vega-Lite calculate задаётся внутри массива
transform:
{
"transform": [
{
"calculate": "datum.a + datum.b",
"as": "sum"
}
]
}
calculate — выражение в языке Vega Expression
Languageas — имя нового поляВ Vega (низкоуровневый формат) используется аналогичная логика:
{
"type": "formula",
"expr": "datum.a + datum.b",
"as": "sum"
}
Несмотря на различие в названии (calculate vs
formula), концепция идентична: вычисление нового поля на
основе текущей записи (datum).
Выражения выполняются для каждой строки данных отдельно. Основной объект контекста:
datum — текущая запись данныхdatum.fieldNamesignalПример:
{
"calculate": "datum.price * datum.quantity",
"as": "total"
}
Каждая строка получает собственное значение total.
Vega Expression Language — это ограниченный, но мощный DSL, похожий на JavaScript, но без доступа к полноценной среде исполнения.
Поддерживаются:
+ - * / %&& || !== != < > <= >=condition ? a : bround(), floor(),
ceil(), pow(), log(),
sqrt()substring(),
indexof(), toUpperCase(),
toLowerCase()number(), string(),
boolean()Частый сценарий — создание производных метрик:
{
"transform": [
{
"calculate": "datum.revenue - datum.cost",
"as": "profit"
}
]
}
Или вычисление коэффициентов:
{
"calculate": "datum.success / datum.total",
"as": "ratio"
}
Важно учитывать отсутствие автоматической защиты от деления на ноль.
Без явной проверки выражение может вернуть Infinity или
NaN.
Безопасный вариант:
{
"calculate": "datum.total == 0 ? 0 : datum.success / datum.total",
"as": "ratio"
}
calculate активно используется для категоризации
данных:
{
"calculate": "datum.value > 100 ? 'high' : 'low'",
"as": "category"
}
Многоуровневые условия:
{
"calculate": "datum.value > 100 ? 'A' : datum.value > 50 ? 'B' : 'C'",
"as": "grade"
}
Такой подход заменяет необходимость предварительной агрегации или классификации.
В Vega Expression Language доступны базовые строковые операции:
{
"calculate": "datum.firstName + ' ' + datum.lastName",
"as": "fullName"
}
Извлечение подстроки:
{
"calculate": "substring(datum.code, 0, 3)",
"as": "prefix"
}
Поиск внутри строки:
{
"calculate": "indexof(datum.email, '@') > -1",
"as": "isEmailValid"
}
Преобразование регистра:
{
"calculate": "toUpperCase(datum.country)",
"as": "countryUpper"
}
Vega не всегда автоматически приводит типы, поэтому
calculate часто используется для явного преобразования:
{
"calculate": "number(datum.value)",
"as": "valueNum"
}
Или:
{
"calculate": "string(datum.id)",
"as": "idStr"
}
Это особенно важно при работе с CSV или JSON, где числовые поля могут приходить строками.
Поддерживаются стандартные математические операции:
{
"calculate": "round(datum.value * 100) / 100",
"as": "rounded"
}
{
"calculate": "pow(datum.x, 2) + pow(datum.y, 2)",
"as": "distanceSquared"
}
{
"calculate": "sqrt(datum.value)",
"as": "root"
}
Несколько calculate могут применяться последовательно.
Каждый следующий шаг может использовать результат предыдущего:
{
"transform": [
{
"calculate": "datum.price * datum.qty",
"as": "total"
},
{
"calculate": "datum.total * 0.2",
"as": "tax"
},
{
"calculate": "datum.total + datum.tax",
"as": "final"
}
]
}
Важно: порядок трансформаций имеет значение, так как поля создаются последовательно.
При работе с датами calculate может применяться для
преобразований времени:
{
"calculate": "toDate(datum.timestamp)",
"as": "date"
}
Или извлечение компонентов (в зависимости от окружения Vega):
{
"calculate": "hours(datum.date)",
"as": "hour"
}
Часто временные операции комбинируются с агрегациями и шкалированием.
Vega-Lite специально ограничивает выражения, чтобы сохранить декларативность. Нельзя:
Поддерживаются только выражения Vega Expression Language.
В Vega (низкий уровень) возможностей больше, но принцип вычислений тот же.
"calculate": "datum.price * datum.amount"
Если amount отсутствует, результат будет
null.
"calculate": "datum.value + 10"
Если value — строка, результат может стать
конкатенацией.
Решение:
"calculate": "number(datum.value) + 10"
"calculate": "datum.a / datum.b"
Без проверки b возможны некорректные значения.
calculate часто применяется перед:
scale)filter)aggregate)mark)Пример подготовки данных для диаграммы:
{
"transform": [
{
"calculate": "datum.sales / datum.target",
"as": "performance"
}
],
"mark": "bar",
"encoding": {
"x": {"field": "performance", "type": "quantitative"},
"y": {"field": "name", "type": "nominal"}
}
}
calculate часто используется вместе с
filter:
{
"transform": [
{
"calculate": "datum.value > 0",
"as": "isPositive"
},
{
"filter": "datum.isPositive"
}
]
}
Такой подход позволяет сначала вычислить признак, затем использовать его для отбора данных.
Хотя calculate оптимизирован, есть ограничения:
Практика:
{
"calculate": "(datum.value - datum.min) / (datum.max - datum.min)",
"as": "normalized"
}
{
"calculate": "datum.score >= 0.5 ? 1 : 0",
"as": "binary"
}
{
"calculate": "datum.value < 10 ? 'small' : datum.value < 50 ? 'medium' : 'large'",
"as": "bucket"
}
calculate выполняет функцию промежуточного слоя между
сырыми данными и визуальными кодировками. Он:
Эта трансформация является фундаментальным инструментом подготовки данных внутри pipeline Vega/Vega-Lite, особенно в сценариях динамической визуальной аналитики.