Форматы данных для иерархических компонентов

Библиотека визуализации данных Nivo опирается на строго определённые форматы иерархических структур, которые напрямую влияют на корректность построения таких компонентов, как деревья, treemap, sunburst и circle packing. Основой служит модель вложенных объектов, соответствующая принципам d3-hierarchy, где каждый узел содержит ссылку на дочерние элементы и описывает часть общей структуры.

Ключевой принцип заключается в том, что иерархические компоненты не работают с плоскими массивами напрямую. Им требуется дерево, в котором каждый уровень вложенности формирует отдельный слой визуализации.


Базовый формат вложенного дерева

Наиболее распространённая форма представления иерархии — вложенный JSON-объект:

const data = {
  name: "root",
  children: [
    {
      name: "branch A",
      children: [
        { name: "leaf A1", value: 10 },
        { name: "leaf A2", value: 20 }
      ]
    },
    {
      name: "branch B",
      children: [
        { name: "leaf B1", value: 15 }
      ]
    }
  ]
};

Основные свойства узлов:

  • name — идентификатор узла, часто используется как ключ отображения
  • children — массив дочерних узлов
  • value — числовое значение (используется в leaf-узлах или агрегируется автоматически)

В компонентах Nivo значение value может быть либо задано явно, либо вычислено через суммирование дочерних узлов.


Агрегация значений в иерархии

Внутренне Nivo использует алгоритмы суммирования, аналогичные d3.sum. Если у родительского узла отсутствует value, оно вычисляется рекурсивно:

  • листовые узлы: фиксированные значения
  • промежуточные узлы: сумма значений потомков

Пример логики:

branch A = leaf A1 + leaf A2
branch B = leaf B1
root = branch A + branch B

Это критически важно для treemap и sunburst, где площадь или угол пропорциональны величине значения.


Формат данных для treemap

Treemap-компонент в Nivo ожидает тот же вложенный формат, но с акцентом на числовые значения:

const treemapData = {
  name: "root",
  children: [
    {
      name: "group 1",
      children: [
        { name: "item 1", value: 100 },
        { name: "item 2", value: 300 }
      ]
    }
  ]
};

Дополнительно поддерживаются параметры:

  • color — фиксированный цвет узла
  • id — уникальный идентификатор (используется для стабильного ключа)

Формат данных для sunburst

Sunburst использует тот же иерархический JSON, но интерпретирует структуру как радиальные сектора.

const sunburstData = {
  name: "total",
  children: [
    {
      name: "segment A",
      children: [
        { name: "A1", value: 40 },
        { name: "A2", value: 60 }
      ]
    }
  ]
};

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


Circle Packing и структурная вложенность

Circle packing требует строгой древовидной структуры без пропусков уровней.

const circleData = {
  name: "root",
  children: [
    {
      name: "cluster 1",
      children: [
        { name: "node 1", value: 5 },
        { name: "node 2", value: 8 }
      ]
    }
  ]
};

Здесь важен не только value, но и глубина вложенности, так как она влияет на радиус и позиционирование окружностей.


Альтернативный формат: плоские данные

Некоторые сценарии требуют хранения данных в плоском виде. В этом случае используется преобразование через d3.stratify, которое поддерживается в экосистеме Nivo.

Пример плоской структуры:

const flatData = [
  { id: "root" },
  { id: "A", parent: "root", value: 0 },
  { id: "B", parent: "root", value: 0 },
  { id: "A1", parent: "A", value: 10 },
  { id: "A2", parent: "A", value: 20 }
];

Принципы преобразования:

  • id — уникальный идентификатор узла
  • parent — ссылка на родительский узел
  • корневой элемент не имеет parent

После преобразования структура становится иерархической и может использоваться всеми tree-компонентами.


Использование d3.stratify в связке с Nivo

import { stratify } from "d3-hierarchy";

const root = stratify()
  .id(d => d.id)
  .parentId(d => d.parent)(flatData);

Результат — объект с вложенными children, совместимый с Nivo.


Типизация структуры данных (TypeScript)

type HierarchyNode = {
  id?: string;
  name?: string;
  value?: number;
  color?: string;
  children?: HierarchyNode[];
};

Для плоской модели:

type FlatNode = {
  id: string;
  parent?: string;
  value?: number;
};

Влияние структуры на визуализацию

Разные компоненты Nivo интерпретируют одни и те же данные по-разному:

  • Treemap — площадь прямоугольников
  • Sunburst — угловые сектора
  • Circle Packing — радиусы окружностей
  • Tree — координатная структура узлов

Несмотря на общий формат, глубина и распределение value радикально изменяют визуальный результат.


Нормализация данных перед подачей в Nivo

Перед использованием иерархии часто выполняется подготовка:

  • удаление узлов с нулевыми значениями
  • агрегация повторяющихся идентификаторов
  • выравнивание глубины дерева
  • обеспечение уникальности id

Пример нормализованного узла:

{
  id: "metrics.cpu",
  name: "CPU",
  value: 72,
  children: []
}

Частичные и разреженные деревья

Иерархии могут содержать пропуски уровней, однако некоторые компоненты Nivo требуют плотной структуры:

{
  name: "A",
  children: [
    {
      name: "B",
      children: [
        { name: "C", value: 1 }
      ]
    }
  ]
}

При отсутствии промежуточных узлов визуализация может изменять геометрию распределения элементов, особенно в radial-компонентах.


Гибридные структуры и метаданные

Узлы могут содержать дополнительные поля, не влияющие на расчёты, но используемые для кастомизации:

{
  name: "node",
  value: 10,
  metadata: {
    region: "EU",
    status: "active"
  }
}

Nivo игнорирует неизвестные поля, но они доступны в кастомных tooltip, слоях и форматтерах.


Поведение при отсутствии value

Если value отсутствует:

  • узел считается контейнером
  • его размер вычисляется по потомкам
  • при отсутствии потомков узел может игнорироваться визуализацией
{ name: "empty branch", children: [] }

Такие узлы часто фильтруются на этапе подготовки данных.


Стабильность идентификаторов

Для корректного обновления анимаций важно поддерживать стабильные идентификаторы:

  • изменение name без изменения структуры приводит к пересозданию узла
  • использование id обеспечивает устойчивость diff-алгоритма
{ id: "node-1", value: 50 }

Иерархия как универсальный контракт данных

Единая модель дерева позволяет использовать один и тот же набор данных в разных визуализациях Nivo без изменения структуры, меняя только способ интерпретации:

  • переход от treemap к sunburst не требует трансформации данных
  • изменение происходит на уровне layout-алгоритма
  • сохраняется семантика родитель-дочерних связей