В Vega модель отображения строится вокруг композиции графических примитивов, где ключевым контейнером выступает group mark. Группа представляет собой самостоятельную сцену со своим набором данных, вычислений и визуальных элементов. Группы могут быть вложенными, образуя древовидную структуру сцены, в которой каждый уровень способен переопределять параметры визуализации.
В Vega-Lite аналогом групп выступают операции компоновки: facet, repeat, concat, а также внутренние преобразования, которые при компиляции превращаются в Vega group marks. Именно на этом уровне возникает ключевая проблема — управление шкалами внутри вложенных областей визуализации.
Вложенные группы создают локальные контексты, в которых могут существовать собственные:
При этом важнейшим аспектом становится поведение шкал при переходе между уровнями вложенности.
Шкала в Vega может быть определена либо на уровне всей сцены, либо внутри конкретной группы. Это определяет область её видимости и привязку к данным.
Глобальная шкала:
scalesЛокальная шкала группы:
marks типа groupПростейшая структура Vega с вложенной группой:
{
"type": "group",
"marks": [
{
"type": "rect",
"encode": {
"enter": {
"x": {"scale": "x", "field": "a"},
"y": {"scale": "y", "field": "b"}
}
}
}
],
"scales": [
{
"name": "x",
"type": "linear",
"domain": {"data": "child", "field": "a"},
"range": "width"
}
]
}
Здесь шкала x существует только в рамках группы и не
конфликтует с одноимёнными шкалами на других уровнях.
Вложенные группы не наследуют шкалы напрямую. Вместо этого происходит передача данных и сигналов, а шкалы пересоздаются в каждом контексте.
Ключевые свойства:
sourcesignalsПример вложенности:
{
"type": "group",
"scales": [
{
"name": "xGlobal",
"type": "linear",
"domain": [0, 100],
"range": "width"
}
],
"marks": [
{
"type": "group",
"from": {"data": "table"},
"scales": [
{
"name": "xLocal",
"type": "linear",
"domain": {"data": "child", "field": "value"},
"range": "width"
}
],
"marks": [
{
"type": "rect",
"encode": {
"enter": {
"x": {"scale": "xLocal", "field": "value"},
"width": {"value": 10}
}
}
}
]
}
]
}
Здесь xGlobal и xLocal существуют
параллельно, но не пересекаются по области применения.
В Vega-Lite вложенные группы чаще всего возникают автоматически при использовании:
facetrepeatconcatlayer (в комбинации с facet)Фасетирование создаёт отдельную группу для каждой подвыборки данных, каждая из которых получает собственный набор шкал.
Пример фасетирования:
{
"data": {"url": "data.csv"},
"facet": {
"row": {"field": "category", "type": "nominal"}
},
"spec": {
"mark": "bar",
"encoding": {
"x": {"field": "value", "type": "quantitative"},
"y": {"field": "name", "type": "nominal"}
}
}
}
При компиляции каждая ячейка фасета превращается в отдельную группу Vega, содержащую:
x и yПри наличии нескольких групп возникает задача согласования шкал между ними. В Vega-Lite для этого используется механизм resolve.
Основные стратегии:
shared — шкалы объединяются между группамиindependent — каждая группа получает собственную
шкалуunion (для domain) — объединение доменовПример:
{
"facet": {
"row": {"field": "category"}
},
"spec": {
"mark": "line",
"encoding": {
"x": {"field": "date", "type": "temporal"},
"y": {"field": "value", "type": "quantitative"}
}
},
"resolve": {
"scale": {
"y": "shared",
"x": "independent"
}
}
}
В этом случае:
Vega-Lite при трансляции в Vega формирует дерево групп, где каждая
композиционная операция становится узлом group.
Типичная трансформация:
facet → group with from + nested group per
facet cellrepeat → multiple child groupsconcat → sequential group layoutlayer → shared group with overlapping marksСтруктура вложенности:
root group
├── facet group A
│ ├── scales A
│ └── marks
├── facet group B
│ ├── scales B
│ └── marks
Каждый узел содержит собственные шкалы, если они не объединены через
resolve.
Шкалы в Vega имеют строго определённую область видимости:
Важно, что одинаковые имена шкал в разных группах не конфликтуют, поскольку resolution происходит по пути доступа, а не по глобальному реестру.
Пример:
{
"type": "group",
"scales": [
{"name": "x", "type": "linear"}
],
"marks": [
{
"type": "group",
"scales": [
{"name": "x", "type": "band"}
]
}
]
}
Здесь две шкалы x существуют в разных контекстах и имеют
разные типы.
При изменении данных в Vega происходит реактивный пересчёт:
Вложенные группы усиливают стоимость пересчёта, так как каждый уровень может содержать независимые шкалы.
Особенно критично это при:
Каждая группа формирует собственную систему координат:
width и height
группыЭто означает, что шкала всегда работает в пределах локального пространства группы, а не глобального canvas.
Вложенные группы применяются для построения:
Каждая такая структура опирается на принцип изоляции шкал, где локальный контекст полностью управляет визуализацией, не влияя на соседние группы.
При использовании layer внутри facet
возникает смешанная модель:
Пример комбинированной структуры:
{
"facet": {
"column": {"field": "group"}
},
"spec": {
"layer": [
{
"mark": "line",
"encoding": {
"x": {"field": "x", "type": "quantitative"},
"y": {"field": "y", "type": "quantitative"}
}
},
{
"mark": "point"
}
]
}
}
Здесь слой существует внутри фасетной группы, но использует её локальные шкалы.
При изменении размеров контейнера пересчитываются:
Вложенные группы добавляют каскадность: изменение родительского
размера влияет на все дочерние шкалы через пересчёт
range.
Особенно важно:
width/height группы