В Vega трансформация pack реализует алгоритм круговой
упаковки иерархических данных на основе подхода circle packing.
Результат работы трансформации — набор узлов с вычисленными координатами
x, y и радиусом r, которые затем
используются для построения компактной вложенной структуры кругов без
пересечений.
В контексте Vega-Lite данный механизм применяется как высокоуровневая абстракция для визуализаций, где требуется отобразить иерархию через вложенные окружности вместо прямоугольных областей (как в treemap).
Алгоритм упакованных кругов решает задачу размещения иерархических узлов таким образом, чтобы:
Формально используется иерархическая модель данных, где каждый узел имеет потомков. Радиусы рассчитываются снизу вверх: от листьев к корню.
Трансформация pack требует иерархических данных
вида:
{
"name": "root",
"children": [
{
"name": "A",
"value": 10
},
{
"name": "B",
"children": [
{ "name": "B1", "value": 5 },
{ "name": "B2", "value": 8 }
]
}
]
}
Ключевые моменты:
children определяет вложенностьvalue определяет размер листового узлаpackВ Vega:
{
"type": "pack",
"field": "value",
"size": [{"signal": "width"}, {"signal": "height"}],
"padding": 2
}
fieldОпределяет поле, по которому вычисляется размер узла.
sizeОпределяет размеры области раскладки:
"size": [width, height]
или через сигналы:
"size": [{"signal": "width"}, {"signal": "height"}]
paddingРасстояние между кругами.
asПереименование выходных полей:
"as": ["x", "y", "r", "depth", "children"]
По умолчанию создаются:
xyrdepthsortКонтролирует порядок размещения узлов.
Пример:
"sort": {"field": "value", "order": "descending"}
Сортировка влияет на компактность и стабильность раскладки.
После применения pack каждый узел получает:
x — координата центра по оси Xy — координата центра по оси Yr — радиус кругаdepth — уровень иерархииchildren — вложенные узлы (внутреннее
представление)Эти значения используются mark-слоем circle:
{
"mark": "circle",
"encoding": {
"x": {"field": "x"},
"y": {"field": "y"},
"size": {"field": "r"}
}
}
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"width": 500,
"height": 500,
"data": [
{
"name": "tree",
"values": [
{
"name": "root",
"children": [
{"name": "A", "value": 10},
{
"name": "B",
"children": [
{"name": "B1", "value": 5},
{"name": "B2", "value": 8}
]
}
]
}
],
"transform": [
{
"type": "stratify",
"key": "name",
"parentKey": "parent"
},
{
"type": "pack",
"field": "value",
"size": [{"signal": "width"}, {"signal": "height"}],
"padding": 3
}
]
}
],
"marks": [
{
"type": "circle",
"from": {"data": "tree"},
"encode": {
"enter": {
"x": {"field": "x"},
"y": {"field": "y"},
"r": {"field": "r"},
"fill": {"value": "steelblue"}
}
}
}
]
}
Трансформация основана на алгоритме d3-hierarchy pack,
который реализует:
Данные преобразуются в дерево.
Каждый родитель получает суммарный размер потомков.
Радиус узла определяется как:
sizeАлгоритм:
Каждый узел имеет центр (x, y), относительно которого
строится окружность.
Если узел содержит потомков:
| Характеристика | pack | treemap |
|---|---|---|
| Геометрия | круги | прямоугольники |
| Плотность | ниже | выше |
| Восприятие иерархии | более визуально естественная | более аналитическая |
| Заполнение пространства | хуже | лучше |
stratify вместе с packЧасто pack требует предварительного преобразования:
{
"type": "stratify",
"key": "id",
"parentKey": "parent"
}
Это позволяет строить дерево из плоских данных:
[
{"id": "root"},
{"id": "A", "parent": "root", "value": 10},
{"id": "B", "parent": "root", "value": 20}
]
При изменении размеров контейнера:
Глубокие иерархии приводят к:
Увеличение padding:
Без sort результат может меняться при каждом
пересчёте.
Сортировка по убыванию:
stratify при плоских данныхpack без иерархииpadding, приводящее к наложениюvaluepack часто комбинируется с:
filter — для отбора узловformula — для вычисления значения узловlookup — для добавления атрибутовcollect — для управления порядкомПример цепочки:
"transform": [
{"type": "filter", "expr": "datum.value > 0"},
{"type": "pack", "field": "value", "padding": 2}
]