Radar-графики в Nivo используют строго определённую структуру данных, основанную на наборах объектов с именованными показателями. Каждая точка на радиальной оси представляет отдельную категорию, а значение внутри объекта определяет величину для этой категории.
Базовая концепция строится вокруг массива объектов, где каждый объект соответствует одной «серии» данных.
Минимальная структура данных для Radar-графика включает:
const data = [
{
taste: 80,
cost: 60,
depth: 70,
id: "product A"
}
]
Каждое поле, кроме id, рассматривается как отдельная ось
(axis). В приведённом примере оси: taste,
cost, depth.
Ключевым моментом является то, что Nivo автоматически интерпретирует все числовые поля, кроме идентификатора серии, как измерения для радиальной диаграммы.
Radar-графики раскрывают свою основную функциональность при сравнении нескольких объектов. В этом случае структура данных расширяется за счёт добавления дополнительных элементов массива.
const data = [
{
taste: 80,
cost: 60,
depth: 70,
id: "product A"
},
{
taste: 65,
cost: 90,
depth: 50,
id: "product B"
},
{
taste: 90,
cost: 40,
depth: 80,
id: "product C"
}
]
Каждый объект массива формирует отдельный полигон на Radar-графике.
Сравнение происходит по одинаковым ключам (taste,
cost, depth).
Поле id выполняет функцию уникального идентификатора
серии. Оно используется для:
Отсутствие или дублирование id приводит к некорректному
отображению серий.
В Nivo Radar явно задаётся список ключей, которые определяют оси графика:
const keys = ["taste", "cost", "depth"]
Эти ключи должны:
Несоответствие ключей приводит к пропускам значений или визуальным разрывам полигона.
Radar-график работает исключительно с числовыми значениями. Поддерживаются:
Пример нормализованного набора:
const data = [
{
taste: 0.8,
cost: 0.6,
depth: 0.7,
id: "normalized A"
},
{
taste: 0.5,
cost: 0.9,
depth: 0.4,
id: "normalized B"
}
]
Использование строк или null-значений приводит к игнорированию точки или некорректной отрисовке.
Все объекты массива должны иметь одинаковую структуру. Допустимая модель:
[
{ id: "A", x: 10, y: 20, z: 30 },
{ id: "B", x: 15, y: 25, z: 35 }
]
Недопустимая модель:
[
{ id: "A", x: 10, y: 20 },
{ id: "B", x: 15, z: 35 }
]
В последнем случае отсутствующие ключи приводят к разрывам в полигоне и неконсистентному отображению.
Часто данные приходят в формате, не совместимом с Radar-графиком. Типичный случай — вложенные структуры:
const raw = [
{
id: "A",
stats: {
taste: 80,
cost: 60,
depth: 70
}
}
]
Для использования в Nivo требуется трансформация:
const data = raw.map(item => ({
id: item.id,
taste: item.stats.taste,
cost: item.stats.cost,
depth: item.stats.depth
}))
При неизвестной заранее структуре данных ключи могут извлекаться динамически:
const keys = Object.keys(data[0]).filter(k => k !== "id")
Такой подход требует строгой гарантии однородности объектов массива.
Radar-график может работать с данными разного масштаба, однако визуальная интерпретация зависит от нормализации.
Пример приведения к процентам:
const normalized = data.map(item => ({
id: item.id,
taste: item.taste / 100,
cost: item.cost / 100,
depth: item.depth / 100
}))
Это позволяет унифицировать отображение при разнородных метриках.
Отсутствующие значения должны быть явно обработаны:
Пример замены:
const safeData = data.map(item => ({
id: item.id,
taste: item.taste ?? 0,
cost: item.cost ?? 0,
depth: item.depth ?? 0
}))
Структура данных тесно связана с параметрами компонента:
indexBy определяет поле идентификатораkeys определяет оси<Radar
data={data}
keys={["taste", "cost", "depth"]}
indexBy="id"
/>
Несоответствие между indexBy и фактическим полем данных
приводит к пустой визуализации.
Основные проблемы возникают при:
idКаждая из этих ошибок напрямую влияет на геометрию полигона и корректность визуального сравнения.
Часто данные поступают в виде таблицы:
const table = [
["product A", 80, 60, 70],
["product B", 65, 90, 50]
]
Преобразование:
const data = table.map(row => ({
id: row[0],
taste: row[1],
cost: row[2],
depth: row[3]
}))
Такой подход позволяет интегрировать Radar-графики в аналитические системы с табличным источником данных.
При наличии большого количества исходных записей часто требуется агрегация:
const grouped = Object.values(
raw.reduce((acc, item) => {
if (!acc[item.id]) {
acc[item.id] = { id: item.id, taste: 0, cost: 0, depth: 0, count: 0 }
}
acc[item.id].taste += item.taste
acc[item.id].cost += item.cost
acc[item.id].depth += item.depth
acc[item.id].count += 1
return acc
}, {})
)
const data = grouped.map(item => ({
id: item.id,
taste: item.taste / item.count,
cost: item.cost / item.count,
depth: item.depth / item.count
}))
Универсальная форма представления сводится к следующей схеме: