extent-трансформация в Vega и Vega-Lite представляет собой вычислительный механизм получения диапазона значений (минимума и максимума) для числовых данных или выражений, часто с учётом группировки. Эта операция лежит на стыке агрегации и подготовки шкал, позволяя динамически определять границы визуализации без явного предварительного вычисления статистики на стороне приложения.
Extent-трансформация вычисляет пару значений:
Результат формируется как массив фиксированной длины из двух элементов:
[min, max]
В отличие от классической агрегации, которая возвращает одно значение на группу, extent возвращает диапазон, что делает её особенно полезной для:
Внутри Vega-Lite extent часто используется как промежуточный шаг, который затем связывается с масштабами (scale domains).
В Vega-Lite трансформация задаётся внутри массива
transform. Простейшая форма выглядит следующим образом:
{
"transform": [
{
"extent": "value",
"signal": "value_extent"
}
]
}
Где:
extent — поле данных или выражениеsignal (или в Vega — output name) — имя результатаВ Vega более низкого уровня конструкция выглядит аналогично, но с явным указанием output-поля:
{
"type": "extent",
"field": "value",
"signal": "value_extent"
}
Наиболее частый сценарий — вычисление min/max по числовому полю.
{
"data": {"name": "table"},
"transform": [
{
"type": "extent",
"field": "price",
"signal": "price_extent"
}
]
}
После выполнения трансформации формируется сигнал:
price_extent = [min(price), max(price)]
Этот сигнал затем может быть использован в шкалах:
{
"scales": [
{
"name": "y",
"type": "linear",
"domain": {"signal": "price_extent"},
"range": "height"
}
]
}
Одно из ключевых применений extent — вычисление диапазонов внутри
групп. В Vega это достигается через groupby.
{
"type": "extent",
"field": "value",
"groupby": ["category"],
"signal": "category_extents"
}
Результатом становится структура вида:
category_extents = [
{category: "A", extent: [minA, maxA]},
{category: "B", extent: [minB, maxB]}
]
Такой формат используется для:
В Vega extent может вычисляться не только по полю, но и по выражению. Это важно для динамических преобразований данных:
{
"type": "extent",
"expr": "datum.a + datum.b",
"signal": "sum_extent"
}
В Vega-Lite аналог достигается через calculate перед
применением extent:
{
"transform": [
{
"calculate": "datum.a + datum.b",
"as": "sum"
},
{
"extent": "sum",
"signal": "sum_extent"
}
]
}
Такая композиция позволяет строить производные метрики без изменения исходного датасета.
Наиболее значимая роль extent — автоматическое формирование domain для шкал. Вместо ручного задания диапазона:
"domain": [0, 100]
используется вычисляемый сигнал:
"domain": {"signal": "value_extent"}
Это обеспечивает:
Extent можно рассматривать как комбинированный агрегат, однако между ними есть принципиальная разница:
| Операция | Результат |
|---|---|
| min | одно значение |
| max | одно значение |
| extent | пара [min, max] |
При этом extent часто эффективнее, чем два отдельных агрегата, поскольку:
В сложных спецификациях extent применяется на разных уровнях:
Пример комбинированного использования:
{
"transform": [
{
"filter": "datum.year > 2000"
},
{
"extent": "value",
"groupby": ["region"],
"signal": "region_extents"
}
]
}
Такой подход позволяет строить независимые шкалы для каждой группы региона.
Extent часто применяется для управления интерактивностью, особенно при:
Пример:
{
"signals": [
{
"name": "brush",
"value": null
}
],
"transform": [
{
"extent": "value",
"signal": "full_extent"
}
]
}
Далее extent может ограничиваться выбранным диапазоном:
filtered_extent = intersection(full_extent, brush)
При работе с большими наборами данных extent становится критической операцией. Оптимизации включают:
В Vega runtime extent выполняется как потоковая редукция:
init: [∞, -∞]
update:
min = Math.min(min, value)
max = Math.max(max, value)
При вычислении диапазона учитываются особенности данных:
null значения игнорируютсяNaN исключаютсяЭто важно при смешанных датасетах, где требуется предварительная очистка через transform:
{
"calculate": "toNumber(datum.value)",
"as": "value_num"
}
Extent редко используется изолированно. Чаще он комбинируется с:
filter — для ограничения диапазона входных данныхcalculate — для производных метрикbin — для гистограммaggregate — для предварительного свёртыванияПример цепочки:
{
"transform": [
{"filter": "datum.active == true"},
{"calculate": "datum.x * datum.y", "as": "metric"},
{"extent": "metric", "signal": "metric_extent"}
]
}
Такая последовательность формирует чистый вычислительный pipeline.
Extent является примером декларативного описания статистической операции. Вместо явного программирования цикла вычисления min/max, спецификация описывает:
Движок выполнения самостоятельно определяет способ вычисления, сохраняя согласованность между данными и визуальными компонентами.