1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/x5017-Learn-D3

В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
Hierarchies.ch.md 23 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 02.06.2025 20:44 467f5f5

Иерархии D3

! [image-20220823162654765](. /image/image-20220823162654765. png) Как использовать D3.js для визуализации иерархических данных (деревьев). В этой статье показано, как создать иерархическую структуру данных из массива данных. Затем используется D3 для визуализации, с использованием различных форматов отображения, включая Tree,Cluster,Treemap,Pack,Partition. Одним из важных шагов при анализе или визуализации данных является их предварительная обработка, например: группировка Предположим, что у нас есть данные о структуре управления в компании (очень простой набор данных):

name boss
Подчиненный 1 у Лao Wang Лao Wang
Подчиненный 2 у Лao Wang Лao Wang
Подчиненный 3 у Лao Wang Лao Wang
Подчиненный 1 у Лao Li Лao Li
Подчиненный 2 у Лao Li Лao Li
Подчиненный 3 у Лao Li Лao Li
Подчиненный 4 у Лao Li Лao Li
Например, вот данные о фильмах:
Можно группировать данные по boss и подсчитывать количество подчиненных у каждого босса:
boss Количество подчиненных
:--- :-------
Лao Li 4
Лao Wang 3
Данные могут иметь иерархическую структуру. Верхний уровень (top level) — это boss (в данном случае Лao Wang и Лao Li); следующий уровень — это их подчиненные.
Иерархическую структуру можно рассматривать как дерево, где корневой узел (здесь определен как CEO), разделяется на верхний уровень (в данном случае боссы).Каждый верхний узел разделяется на группу второго уровня (в данном случае подчинённые), и так далее:
! [image-20220824113057496](. /image/image-20220824113057496. png)

Верхний уровень элементов (узлов) называется root node (корневым узлом). Самый нижний элемент называется листом или листовым узлом. Разумеется, для разных данных можно использовать различные способы группировки (агрегации). В этой статье будут рассмотрены различные способы визуализации иерархических данных, включая Tree, Cluster, Treemap, Pack, Partition

Создание иерархической структуры из массива данных

Возьмём, например, данные из приведённого выше примера:

let Structure = [
 {
   "name":"Подчинённый 1 у Лao Wang",
   "boss":"Лao Wang",
 },
 {
   "name":"Подчинённый 2 у Лao Wang",
   "boss":"Лao Wang",
 },
 {
   "name":"Подчинённый 3 у Лao Wang",
   "boss":"Лao Wang",
 },
 {
   "name":"Подчинённый 1 у Лao Li",
   "boss":"Лao Li",
 },
 {
   "name":"Подчинённый 2 у Лao Li",
   "boss":"Лao Li",
 },
 {
   "name":"Подчинённый 3 у Лao Li",
   "boss":"Лao Li",
 },
]

Можно использовать ! [image-20220824113524559](. /image/image-20220824113524559. png) функцию для группировки данных по любому классификационному атрибуту. Первый параметр . rollup — это данные, которые нужно группировать (итерируемый массив). Вторым параметром является функция reduce. Это функция, которая принимает массив значений и выводит одно значение. Она проходит по массиву и суммирует один из его атрибутов.Дальнейшие параметры — это функции, которые указывают атрибуты для группировки. (последовательная группировка) Давайте сгруппируем элементы по группам boss и name; суммируем length

let groups = d3.rollup(Structure,
               function(d) { return d.length; }, // здесь length равно 1;
               function(d) { return d.boss; },
               function(d) { return d.name; },
              );

![](. /image/image-20220824114112940.png) В приведенном выше примере данные группируются и суммируются с помощью d3.rollup. d3.rollup возвращает вложенный объект Map. Map можно получить с помощью .get:

groups.get("Старший");
// Map(3) {'Старший подчиненный 1' => 1, 'Старший подчиненный 2' => 1, 'Старший подчиненный 3' => 1}
groups.get("Старший").get("Старший подчиненный 1")
// 1 возвращает сумму

d3.hierarchy(иерархия)

Внутри D3 есть функция для создания структуры иерархических данных. Создается путем вызова d3.hierarchy и передачи в него объекта Map, созданного с помощью d3.rollup:

let groups = d3.rollup(Structure,
               function(d) { return d.length; },
               function(d) { return d.boss; },
               function(d) { return d.name; },
              );
let root = d3.hierarchy(groups);

![](. /image/image-20220824114727198.png)

В базовом обычном JavaScript объекте определены различные свойства и методы, предоставляющие дополнительные функции. Каждый узел имеет свойства: data, children, depth и height. parent data — это Map, переданный в d3.hierarchy. Обычно значение этого свойства не требуется, так как иерархия hierarchy делает данные доступными через свойства children и value.children — это массив, содержащий подузлы. depth и height указывают глубину и высоту узла в иерархии. (Глубина корневого узла равна нулю, высота листового узла равна нулю.) parent — это родительский узел текущего узла. Листовые узлы выглядят так: Можно видеть, что свойство data содержит сводные значения. Если сводное значение является суммой или подсчетом, можно использовать метод .sum иерархии для возврата значения в root:

let groups = d3.rollup(Structure,
                        function(d) { return d.length; },
                        function(d) { return d.boss; },
                        function(d) { return d.name; },
                       );
let root = d3.hierarchy(groups);
root.sum(function(d) {
  return d[1];
});

Метод .sum принимает функцию-акцессор, первый аргумент которой является свойством data узла. Функция-акцессор возвращает значение, которое требуется сложить.

Обратите внимание, что каждый листовой узел теперь имеет свойство value, равное сумме значений. Например:

Нелистовые узлы также имеют свойство value, равное сумме значений их дочерних узлов.

Каждый узел в D3-иерархии имеет удобные методы, такие как .descendants, .ancestors и .links.

.descendants возвращает массив, содержащий узел и его дочерние узлы.

.ancestors возвращает массив, содержащий узел и его родителей (до корневого узла).

.links возвращает массив, содержащий пары связей между узлами.links` возвращает массив объектов, представляющих соединения между узлами и их дочерними узлами, до листовых узлов.

Визуализация иерархии

Существует несколько методов размещения для визуализации иерархии, включая:

Tree

! [image-20220824141237713](. /image/image-20220824141237713. png)

https://codepen. io/wantnocode/pen/BarEdxY

Treemap

! [image-20220824154122632](. /image/image-20220824154122632. png)

https://codepen. io/wantnocode/pen/gOeyxJe? editors=1111

Pack

! [image-20220824151902200](. /image/image-20220824151902200. png)

https://codepen. io/wantnocode/pen/dymLVaj

partition

! [image-20220824153940570](. /image/image-20220824153940570. png)

https://codepen. io/wantnocode/pen/MWVROgJ

D3 использует функции размещения для выполнения вышеупомянутых форм визуализации. Эти функции принимают структуру d3. hierarchy и добавляют визуальные переменные отображения, такие как положение и размер.

Например, размещение дерева добавляет значения x и y к каждому узлу, чтобы узлы образовывали форму дерева.

В этой главе мы рассмотрим размещения tree, cluster, treemap, pack и partition.

Tree Layout (Размещение дерева)

Размещение узлов иерархии в виде дерева.

! [image-20220824141237713](. /image/image-20220824141237713. png)

https://codepen. io/wantnocode/pen/BarEdxY

Сначала создайте функцию размещения дерева с помощью d3. tree():

var treeLayout = d3. tree();

d3. tree() возвращает функцию размещения, которую можно передать объекту иерархии.

Размер дерева можно настроить с помощью команды . size:

treeLayout. size([700, 200]);
```> ```Затем можно вызвать `treeLayout`, передав в него определенный выше объект иерархии `root`:
treeLayout(root);

Это запишет значения x и y на каждом узле. Рисование узлов:

  • Используйте root.descendants() для получения массива всех узлов
  • Соедините этот массив с кругами (или любым другим типом SVG-элемента)
  • Используйте x и y для позиционирования кругов Для рисования связей:
  • Используйте root.links() для получения массива всех связей
  • Соедините массив с элементами line (или path)
  • Используйте x и y атрибуты source и target для позиционирования line

root.links() возвращает массив, каждый элемент которого представляет объект с двумя свойствами: source и target, представляющими источник и целевую точку связи соответственно.

// Связи
d3.select('svg g')
 .selectAll('line')
 .data(root.links())
 .join('line')
 .attr('x1', function(d) {return d.source.x;})
 .attr('y1', function(d) {return d.source.y;})
 .attr('x2', function(d) {return d.target.x;})
 .attr('y2', function(d) {return d.target.y;});
// Узлы
d3.select('svg g')
 .selectAll('circle')
 .data(root.descendants())
 .join('circle')
 .attr('cx', function(d) {return d.x;})
 .attr('cy', function(d) {return d.y;})
 .attr('r', 4);
 ### Кластерная разметка (Cluster Layout)

Разметка очень похожа на разметку cluster, но основное отличие заключается в том, что все листовые узлы располагаются на одной глубине.

var clusterLayout = d3.cluster()
 .size([400, 200]);
var root = d3.hierarchy(data);
clusterLayout(root);

https://codepen.io/wantnocode/pen/rNdbzvX?editors=1111

Treemap Layout (Treemap Layout)

Для создания функции разметки treemap вызовите d3.treemap():

var treemapLayout = d3.treemap();

Как и раньше, вы можете настроить разметку:

treemapLayout
 .size([400, 200])
 .paddingOuter(10);

Перед применением этой разметки к вашей иерархии вам необходимо вызвать .sum() на иерархии. Это пройдет по дереву и установит .value для каждого узла как сумму его подузлов:

root.sum(function(d) {
 return d.value;
});

Обратите внимание, что в .sum() передается функция-посетитель для указания свойства, которое требуется суммировать. Теперь вы можете вызвать treemapLayout, передав в него определенный выше объект иерархии root:

treemapLayout(root);

Функция размещения дерева добавляет 4 атрибута x0, y0, x1 и y1 к каждому узлу, которые определяют размер каждого прямоугольника в дереве. Теперь вы можете связать узлы с элементами rect и обновить их атрибуты x, y, width и height:

d3.select('svg g')
  .selectAll('rect')
  .data(root.descendants())
  .join('rect')
  .attr('x', function(d) { return d.x0; })
  .attr('y', function(d) { return d.y0; })
  .attr('width', function(d) { return d.x1 - d.x0; })
  .attr('height', function(d) { return d.y1 - d.y0; })

Если вы хотите добавить метки в каждый прямоугольник, вы можете добавить элементы g в массив и добавить элементы rect и text к каждому g:

var nodes = d3.select('svg g')
  .selectAll('g')
  .data(rootNode.descendants())
  .join('g')
  .attr('transform', function(d) {return 'translate(' + [d.x0, d.y0] + ')'})
nodes
  .append('rect')
  .attr('width', function(d) { return d.x1 - d.x0; })
  .attr('height', function(d) { return d.y1 - d.y0; })
nodes
  .append('text')
  .attr('dx', 4)
  .attr('dy', 4)
  .text(function(d) { return d.data.name; })
``````javascript
.attr('dy', 14)
   .text(function(d) {
     return d.data.name;
   })
`treemap` может быть настроен несколькими способами:
- Полярность между узлами и их дочерними узлами может быть установлена с помощью `.paddingOuter`
- Полярность между братскими узлами может быть установлена с помощью `.paddingInner`
- Оба полярности могут быть установлены одновременно с помощью `.padding`
- Внешняя полярность может быть установлена с помощью `.paddingTop`, `.paddingBottom`, `.paddingLeft` и `.paddingRight`.
Треемап имеет не одну, а несколько стратегий размещения прямоугольников. D3 имеет встроенные стратегии, такие как `treemapBinary`, `treemapDice` и `treemapSquarify`.
`treemapBinary` стремится к балансу между горизонтальными и вертикальными разделами, `treemapDice` чередует горизонтальные и вертикальные разделы, а `treemapSquarify` позволяет аспектному соотношению прямоугольников изменяться.
Вы можете выбрать стратегию размещения с помощью метода `.tile`:

treemapLayout.tile(d3.treemapDice)

### Обертка размещения (Pack Layout)
`Pack`-размещение похоже на `Tree`-размещение, но **круги** используются для представления узлов.
   ![](./image/image-20220824155621105.png)
   Функция размещения `Pack` создается с помощью команды `d3.pack()`:

var packLayout = d3.pack();

Как и раньше, размер можно настроить, передавая массив `[width, height]` методу `.size`:

packLayout.size([300, 300]);

Как и в случае с `treemap`, необходимо вызвать `.sum()` **до применения размещения**:

rootNode.sum(function(d) { return d.value; }); packLayout(rootNode);

```  Размещение `pack` добавляет атрибуты `x` и `y` для каждого узла.
Теперь можно связать элементы `circle` с дочерними узлами `root`:

d3.select('svg g') .selectAll('circle') .data(rootNode.descendants()) .join('circle') .attr('cx', function(d) { return d.x; }) .attr('cy', function(d) { return d.y; }) .attr('r', function(d) { return d.r; })

Можно создать элементы для каждого дочернего узла, добавив метки:

var nodes = d3.select('svg g') .selectAll('g') .data(rootNode.descendants()) .join('g') .attr('transform', function(d) {return 'translate(' + [d.x, d.y] + ')'}); nodes .append('circle') .attr('r', function(d) { return d.r; }) nodes .append('text') .attr('dy', 4) .text(function(d) { return d.children === undefined ? d.data.name : ''; })

!  [image-20220824151749759](.  /image/image-20220824151749759.  png)
Размер пустого пространства вокруг каждого круга можно настроить с помощью метода `.padding()`:

packLayout.padding(20);

> Обратите внимание, что настройка должна быть выполнена до применения размещения `packLayout()`;
!  [image-20220824151902200](.  /image/image-20220824151902200.  png)
> https://codepen.io/wantnocode/pen/dymLVaj
### Partition layout (размещение разделения)
Размещение `partition` делит прямоугольное пространство на слои, каждый из которых представляет собой уровень в иерархии. Для каждого узла в слое каждый слой делится дальше:
!  [img](https://www.d3indepth.com/img/layouts/partition.png)
Функция размещения разделения создается с помощью команды `d3.partition()`:

var partitionLayout = d3.partition();

Как и раньше, размер можно настроить, передавая массив `[width, height]` методу `.size`:

partitionLayout.size([400, 200]); ``` Как и в случае с treemap, необходимо вызвать `.sum()` до применения размещения:

rootNode.sum(function(d) {
  return d.value;
});
partitionLayout(rootNode);
```Теперь можно соединить элемент `rect` с каждым подузлом `root`:

d3.select('svg g') .selectAll('rect') .data(rootNode.descendants()) .join('rect') .attr('x', function(d) { return d.x0; }) .attr('y', function(d) { return d.y0; }) .attr('width', function(d) { return d.x1 - d.x0; }) .attr('height', function(d) { return d.y1 - d.y0; });

  ![](./image/image-20220824153116688.png)
  Для добавления отступа между узлами можно использовать метод `.padding()`:

partitionLayout.padding(2);

  ![](./image/image-20220824153042215.png)
  > https://codepen.io/wantnocode/pen/WNzWZWv
  Если нужно изменить направление разбиения, чтобы слои шли слева направо, можно поменять местами `x0` и `y0` с `x1` и `y1` в элементе `rect`:

.attr('x', function(d) { return d.y0; }) .attr('y', function(d) { return d.x0; }) .attr('width', function(d) { return d.y1 - d.y0; }) .attr('height', function(d) { return d.x1 - d.x0; });

  ![](./image/image-20220824153503496.png)
  > https://codepen.io/wantnocode/pen/ZExZXde
  Также можно отобразить `x` как угол поворота, а `y` как радиус для создания солнечной диаграммы разбиения:
    ```
d3.arc()
  .startAngle(function(d) { return d.x0; })
  .endAngle(function(d) { return d.x1; })
  .innerRadius(function(d) { return d.y0; })
  .outerRadius(function(d) { return d.y1; });
 ![](./image/image-20220824153940570.png)

https://codepen.io/wantnocode/pen/MWVROgJ

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/x5017-Learn-D3.git
git@api.gitlife.ru:oschina-mirror/x5017-Learn-D3.git
oschina-mirror
x5017-Learn-D3
x5017-Learn-D3
main