Трансформация formula в Vega-Lite и Vega предназначена
для вычисления новых полей на основе существующих данных. Она позволяет
выполнять математические операции, объединять строки, преобразовывать
значения, вычислять производные показатели и подготавливать данные
непосредственно внутри спецификации визуализации.
formula особенно полезна в ситуациях, когда:
В Vega-Lite трансформация располагается внутри массива
transform.
{
"transform": [
{
"formula": "datum.price * datum.count",
"as": "total"
}
]
}
| Параметр | Назначение |
|---|---|
formula |
Выражение для вычисления |
as |
Имя нового поля |
Трансформация проходит по каждой строке набора данных и вычисляет
выражение из formula.
Исходные данные:
[
{ "product": "Laptop", "price": 1200, "count": 2 },
{ "product": "Mouse", "price": 25, "count": 5 }
]
После применения:
{
"formula": "datum.price * datum.count",
"as": "total"
}
Результат:
[
{ "product": "Laptop", "price": 1200, "count": 2, "total": 2400 },
{ "product": "Mouse", "price": 25, "count": 5, "total": 125 }
]
datumВнутри выражения используется специальный объект
datum.
Он представляет текущую строку данных.
datum.price
datum.count
datum.category
Пример:
{
"formula": "datum.income - datum.expenses",
"as": "profit"
}
{
"formula": "datum.a + datum.b",
"as": "sum"
}
{
"formula": "datum.revenue - datum.costs",
"as": "profit"
}
{
"formula": "datum.price * datum.quantity",
"as": "total"
}
{
"formula": "datum.sales / datum.users",
"as": "avg"
}
{
"formula": "datum.value % 2",
"as": "remainder"
}
Vega поддерживает большое количество встроенных математических функций.
{
"formula": "round(datum.value)",
"as": "rounded"
}
{
"formula": "round(datum.price * 100) / 100",
"as": "roundedPrice"
}
{
"formula": "max(datum.a, datum.b)",
"as": "maxValue"
}
{
"formula": "min(datum.a, datum.b)",
"as": "minValue"
}
{
"formula": "pow(datum.value, 2)",
"as": "square"
}
{
"formula": "sqrt(datum.value)",
"as": "root"
}
{
"formula": "log(datum.value)",
"as": "logValue"
}
{
"formula": "datum.firstName + ' ' + datum.lastName",
"as": "fullName"
}
{
"formula": "'$' + datum.price",
"as": "priceLabel"
}
{
"formula": "upper(datum.country)",
"as": "countryUpper"
}
{
"formula": "lower(datum.category)",
"as": "categoryLower"
}
{
"formula": "length(datum.name)",
"as": "nameLength"
}
В formula поддерживается тернарный оператор.
{
"formula": "datum.value > 100 ? 'big' : 'small'",
"as": "group"
}
{
"formula": "datum.score >= 90 ? 'A' : datum.score >= 75 ? 'B' : 'C'",
"as": "grade"
}
{
"formula": "datum.age >= 18 && datum.age <= 60",
"as": "workingAge"
}
{
"formula": "datum.sales > 1000 && datum.region === 'EU'",
"as": "important"
}
{
"formula": "datum.role === 'admin' || datum.role === 'manager'",
"as": "privileged"
}
Трансформация formula часто применяется для вычисления
временных характеристик.
{
"formula": "year(datum.date)",
"as": "year"
}
{
"formula": "month(datum.date)",
"as": "month"
}
{
"formula": "day(datum.date)",
"as": "weekday"
}
{
"formula": "hours(datum.date)",
"as": "hour"
}
Одно из важнейших применений formula — категоризация
данных.
{
"formula": "datum.income < 3000 ? 'low' : datum.income < 10000 ? 'middle' : 'high'",
"as": "incomeLevel"
}
{
"formula": "datum.age < 18 ? 'child' : datum.age < 60 ? 'adult' : 'senior'",
"as": "ageGroup"
}
formula часто используется совместно с
color.
{
"transform": [
{
"formula": "datum.profit > 0 ? 'profit' : 'loss'",
"as": "status"
}
],
"encoding": {
"color": {
"field": "status",
"type": "nominal"
}
}
}
{
"formula": "(datum.current / datum.target) * 100",
"as": "progress"
}
{
"formula": "datum.sales / datum.totalSales",
"as": "share"
}
formula редко используется изолированно. Обычно она
работает совместно с:
filteraggregatecalculatewindowjoinaggregateaggregate +
formula{
"transform": [
{
"aggregate": [
{
"op": "sum",
"field": "sales",
"as": "totalSales"
}
],
"groupby": ["category"]
},
{
"formula": "datum.totalSales * 0.2",
"as": "tax"
}
]
}
joinaggregate
+ formulaОдин из самых распространённых сценариев.
{
"transform": [
{
"joinaggregate": [
{
"op": "sum",
"field": "sales",
"as": "globalSales"
}
]
},
{
"formula": "datum.sales / datum.globalSales",
"as": "share"
}
]
}
window +
formula{
"transform": [
{
"window": [
{
"op": "rank",
"as": "rank"
}
],
"sort": [
{
"field": "sales",
"order": "descending"
}
]
},
{
"formula": "datum.rank <= 10 ? 'top' : 'other'",
"as": "group"
}
]
}
[
{ "product": "A", "sales": 120, "cost": 80 },
{ "product": "B", "sales": 200, "cost": 140 },
{ "product": "C", "sales": 50, "cost": 70 }
]
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": [
{ "product": "A", "sales": 120, "cost": 80 },
{ "product": "B", "sales": 200, "cost": 140 },
{ "product": "C", "sales": 50, "cost": 70 }
]
},
"transform": [
{
"formula": "datum.sales - datum.cost",
"as": "profit"
},
{
"formula": "datum.profit > 0 ? 'positive' : 'negative'",
"as": "status"
}
],
"mark": "bar",
"encoding": {
"x": {
"field": "product",
"type": "nominal"
},
"y": {
"field": "profit",
"type": "quantitative"
},
"color": {
"field": "status",
"type": "nominal"
}
}
}
formula от
calculateВ Vega-Lite термин calculate используется как псевдоним
formula.
Следующие варианты эквивалентны:
{
"calculate": "datum.a + datum.b",
"as": "sum"
}
{
"formula": "datum.a + datum.b",
"as": "sum"
}
Однако в документации Vega чаще используется formula, а
в Vega-Lite — calculate.
Порядок трансформаций критически важен.
"transform": [
{
"joinaggregate": [
{
"op": "sum",
"field": "sales",
"as": "total"
}
]
},
{
"formula": "datum.sales / datum.total",
"as": "share"
}
]
"transform": [
{
"formula": "datum.sales / datum.total",
"as": "share"
},
{
"joinaggregate": [
{
"op": "sum",
"field": "sales",
"as": "total"
}
]
}
]
Во втором случае поле total ещё не существует на момент
вычисления.
null и
undefinednull{
"formula": "datum.value == null ? 0 : datum.value",
"as": "safeValue"
}
{
"formula": "datum.count === 0 ? 0 : datum.total / datum.count",
"as": "avg"
}
Флаги часто используются для интерактивности и подсветки.
{
"formula": "datum.sales > 1000",
"as": "highlight"
}
{
"formula": "test(/admin/i, datum.role)",
"as": "isAdmin"
}
{
"formula": "'https://example.com/user/' + datum.id",
"as": "profileUrl"
}
{
"formula": "(datum.value - datum.min) / (datum.max - datum.min)",
"as": "normalized"
}
{
"formula": "log(datum.population)",
"as": "logPopulation"
}
{
"formula": "toNumber(datum.value)",
"as": "numericValue"
}
{
"formula": "toString(datum.id)",
"as": "idString"
}
{
"formula": "toDate(datum.timestamp)",
"as": "date"
}
Трансформация formula выполняется для каждой строки
данных. На больших наборах данных сложные вычисления могут существенно
влиять на производительность.
Особенно затратны:
При больших объёмах данных вычисления лучше переносить на сервер или этап предварительной обработки.
datumНеправильно:
{
"formula": "price * count",
"as": "total"
}
Правильно:
{
"formula": "datum.price * datum.count",
"as": "total"
}
{
"formula": "datum.sales / datum.totalSales",
"as": "share"
}
Если totalSales отсутствует, результатом станет
NaN.
{
"formula": "datum.total / datum.count",
"as": "avg"
}
Требуется проверка:
{
"formula": "datum.count === 0 ? 0 : datum.total / datum.count",
"as": "avg"
}
{
"transform": [
{
"joinaggregate": [
{
"op": "sum",
"field": "sales",
"as": "allSales"
}
]
},
{
"formula": "datum.sales - datum.costs",
"as": "profit"
},
{
"formula": "datum.sales / datum.allSales",
"as": "share"
},
{
"formula": "datum.profit > 0 ? 'profit' : 'loss'",
"as": "status"
},
{
"formula": "round(datum.share * 100)",
"as": "sharePercent"
}
]
}
В этом примере:
Сложные выражения ухудшают читаемость.
Плохо:
{
"formula": "(datum.a + datum.b - datum.c) / (datum.d * datum.e) > 10 ? 'x' : 'y'",
"as": "result"
}
Лучше разбивать вычисления:
{
"formula": "datum.a + datum.b - datum.c",
"as": "temp"
}
{
"formula": "datum.temp / (datum.d * datum.e)",
"as": "ratio"
}
{
"formula": "datum.ratio > 10 ? 'x' : 'y'",
"as": "result"
}
formulaТрансформация особенно эффективна для:
formula использовать нежелательноТрансформация не подходит для:
В подобных случаях подготовка данных выполняется заранее — на сервере, в SQL, Pandas, Spark или ETL-пайплайне.