Многие визуализации в Nivo основаны на работе с несколькими наборами данных, где каждая серия представляет отдельную линию, столбец или слой. Подход к организации таких данных определяет корректность отображения графика, поведение легенд, осей и интерактивных элементов.
Основной принцип многосерийной структуры заключается в разделении данных по ключам серий и унифицированной шкале значений по оси X.
Для линейных графиков используется структура:
const data = [
{
id: 'Серия A',
color: 'hsl(210, 70%, 50%)',
data: [
{ x: 'Янв', y: 10 },
{ x: 'Фев', y: 20 },
{ x: 'Мар', y: 15 }
]
},
{
id: 'Серия B',
color: 'hsl(120, 70%, 50%)',
data: [
{ x: 'Янв', y: 5 },
{ x: 'Фев', y: 12 },
{ x: 'Мар', y: 8 }
]
}
]
Каждый объект верхнего уровня представляет отдельную серию. Поле
id используется для легенд, подсказок и идентификации
линии. Вложенный массив data определяет точки.
Компонент ResponsiveLine из Nivo предназначен для
отображения нескольких линий на одном графике.
import { ResponsiveLine } from '@nivo/line'
const MyChart = () => (
<ResponsiveLine
data={data}
margin={{ top: 50, right: 110, bottom: 50, left: 60 }}
xScale={{ type: 'point' }}
yScale={{
type: 'linear',
min: 'auto',
max: 'auto',
stacked: false
}}
axisBottom={{
tickSize: 5,
tickPadding: 5,
tickRotation: 0
}}
axisLeft={{
tickSize: 5,
tickPadding: 5,
tickRotation: 0
}}
pointSize={10}
pointBorderWidth={2}
useMesh={true}
/>
)
Ключевой момент заключается в параметре stacked. При
значении false серии отображаются независимо, при
true происходит суммирование значений по оси Y.
Для корректного отображения нескольких серий необходимо обеспечить
идентичные значения x. Если одна серия содержит значение,
отсутствующее в другой, график может отображать разрывы или игнорировать
точки.
Корректная нормализация:
const normalizedData = [
{
id: 'A',
data: [
{ x: '2024-01', y: 30 },
{ x: '2024-02', y: 40 }
]
},
{
id: 'B',
data: [
{ x: '2024-01', y: 20 },
{ x: '2024-02', y: 25 }
]
}
]
Нежелательная структура:
// разные X в сериях
[
{
id: 'A',
data: [{ x: 'Jan', y: 10 }]
},
{
id: 'B',
data: [{ x: 'Feb', y: 20 }]
}
]
Такая конфигурация нарушает сопоставление точек.
Стекированные графики позволяют отображать вклад каждой серии в общий итог.
<ResponsiveLine
data={data}
yScale={{
type: 'linear',
stacked: true
}}
/>
В этом режиме значения каждой следующей серии добавляются к предыдущей, формируя накопительную кривую.
Особенности поведения:
Для категориальных данных используется
ResponsiveBar.
Структура данных отличается: каждая строка представляет категорию, а ключи — серии.
const data = [
{
month: 'Янв',
A: 10,
B: 20
},
{
month: 'Фев',
A: 15,
B: 25
}
]
Конфигурация:
import { ResponsiveBar } from '@nivo/bar'
const MyBar = () => (
<ResponsiveBar
data={data}
keys={['A', 'B']}
indexBy="month"
margin={{ top: 50, right: 130, bottom: 50, left: 60 }}
padding={0.3}
groupMode="grouped"
/>
)
Параметр keys определяет серии, которые будут
отображаться как отдельные столбцы внутри категории.
Два основных режима:
Отображение серий рядом:
groupMode="grouped"
Характеристики:
groupMode="stacked"
Особенности:
Часто набор серий формируется динамически, например из API.
const keys = ['desktop', 'mobile', 'tablet']
const data = [
{ day: 'Mon', desktop: 120, mobile: 80, tablet: 30 },
{ day: 'Tue', desktop: 100, mobile: 90, tablet: 40 }
]
Передача:
<ResponsiveBar
data={data}
keys={keys}
indexBy="day"
/>
Добавление новой серии не требует изменения структуры графика, только
массива keys.
Area-графики используют ту же структуру, что и line chart.
import { ResponsiveAreaBump } from '@nivo/bump'
или
import { ResponsiveArea } from '@nivo/area'
Пример:
<ResponsiveArea
data={data}
margin={{ top: 50, right: 110, bottom: 50, left: 60 }}
xScale={{ type: 'point' }}
yScale={{ type: 'linear', stacked: true }}
/>
Особенности многосерийного area:
При увеличении количества серий легенда становится ключевым элементом интерфейса.
legends={[
{
anchor: 'bottom-right',
direction: 'column',
translateX: 100,
itemWidth: 80,
itemHeight: 20,
symbolSize: 12
}
]}
Каждый id серии автоматически отображается в легенде.
Цвет можно задать явно или позволить системе распределить палитру.
Цветовая схема может быть статической или динамической.
colors={{ scheme: 'nivo' }}
или
colors={(serie) => {
if (serie.id === 'A') return '#ff0000'
return '#00ff00'
}}
При большом количестве серий предпочтительнее использование схем:
nivocategory10set2Tooltip получает доступ ко всей серии и конкретной точке:
tooltip={({ point }) => (
<div>
<strong>{point.seriesId}</strong>
<div>X: {point.data.x}</div>
<div>Y: {point.data.y}</div>
</div>
)}
В случае Bar-чартов tooltip получает значение ключа внутри категории.
Часто требуется скрытие отдельных серий.
const [hidden, setHidden] = useState(['B'])
const filteredData = data.map(serie => ({
...serie,
data: hidden.includes(serie.id) ? [] : serie.data
}))
Более оптимальный подход — управление через легенду и события клика:
legends={[
{
onClick: (datum) => {
toggleSerie(datum.id)
}
}
]}
В Bar-чартах отсутствие ключа в объекте приводит к нулевому значению.
{ month: 'Jan', A: 10 } // B отсутствует → 0
В Line-чартах несортированный x приводит к ломанию
линии.
Смешение числовых и категориальных значений на X-оси нарушает интерполяцию.
При увеличении числа серий возникают проблемы производительности:
Используются подходы:
Некоторые компоненты позволяют анализировать корреляции между сериями:
При этом важно сохранять одинаковую временную или категориальную шкалу, иначе сравнение теряет смысл.
setData(prev => [
...prev,
{
id: 'New Serie',
data: [...]
}
])
Удаление:
setData(prev => prev.filter(s => s.id !== 'A'))
Такая модель используется при построении интерактивных дашбордов, где набор метрик изменяется во времени.