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

Линейные графики в Nivo ожидают строго определённую структуру данных, которая напрямую влияет на корректность построения осей, интерполяцию значений и поведение tooltip’ов. Библиотека @nivo/line не ограничивается одним форматом: допускается несколько моделей представления данных, но каждая из них должна соответствовать внутренним правилам маппинга x и y.

Основная модель, используемая в большинстве случаев, представляет данные как массив серий. Каждая серия — это отдельная линия на графике.

const data = [
  {
    id: "temperature",
    data: [
      { x: 0, y: 12 },
      { x: 1, y: 15 },
      { x: 2, y: 14 }
    ]
  }
];

Ключевые элементы структуры:

  • id — уникальный идентификатор серии
  • data — массив точек
  • x — значение по оси X
  • y — значение по оси Y

Такой формат считается каноническим и используется по умолчанию без дополнительных настроек.

Типизация точек: числовые значения

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

const data = [
  {
    id: "users",
    data: [
      { x: 1, y: 10 },
      { x: 2, y: 25 },
      { x: 3, y: 18 }
    ]
  }
];

Особенности:

  • ось X интерпретируется как количественная
  • возможно использование линейной шкалы (linear scale)
  • отсутствует необходимость дополнительного парсинга

Этот формат наиболее производительный при рендеринге большого количества точек.

Использование строковых значений на оси X

Nivo поддерживает категориальные данные, где x — строка. Это переключает поведение оси на категориальное отображение.

const data = [
  {
    id: "sales",
    data: [
      { x: "янв", y: 30 },
      { x: "фев", y: 45 },
      { x: "мар", y: 28 }
    ]
  }
];

При таком формате:

  • порядок категорий определяется порядком массива
  • ось X становится дискретной
  • интерполяция между точками отсутствует

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

Дата и временные ряды

Для временных рядов используется формат Date или строка, приводимая к дате через парсер.

Использование объектов Date

const data = [
  {
    id: "traffic",
    data: [
      { x: new Date(2024, 0, 1), y: 120 },
      { x: new Date(2024, 0, 2), y: 135 },
      { x: new Date(2024, 0, 3), y: 90 }
    ]
  }
];

Использование ISO-строк

const data = [
  {
    id: "traffic",
    data: [
      { x: "2024-01-01", y: 120 },
      { x: "2024-01-02", y: 135 },
      { x: "2024-01-03", y: 90 }
    ]
  }
];

При использовании строк важно задать xScale:

xScale={{
  type: "time",
  format: "%Y-%m-%d",
  precision: "day"
}}

Особенности временных данных:

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

Множественные серии

Одно из ключевых преимуществ Line chart в Nivo — поддержка нескольких линий в одном графике.

const data = [
  {
    id: "desktop",
    data: [
      { x: "янв", y: 120 },
      { x: "фев", y: 132 },
      { x: "мар", y: 101 }
    ]
  },
  {
    id: "mobile",
    data: [
      { x: "янв", y: 90 },
      { x: "фев", y: 110 },
      { x: "мар", y: 95 }
    ]
  }
];

Особенности обработки:

  • каждая серия рисуется независимо
  • объединение точек по оси X происходит автоматически
  • отсутствующие значения интерпретируются как разрывы линии (если не включена интерполяция)

Разреженные данные и пропуски

В реальных данных часто встречаются пропуски. Nivo не требует полного покрытия оси X.

const data = [
  {
    id: "sensor",
    data: [
      { x: 1, y: 10 },
      { x: 2, y: null },
      { x: 3, y: 14 }
    ]
  }
];

Поведение при null:

  • точка пропускается
  • линия разрывается
  • tooltip для точки не отображается

Альтернативный вариант — полное отсутствие точки в массиве:

data: [
  { x: 1, y: 10 },
  { x: 3, y: 14 }
]

В этом случае библиотека также интерпретирует разрыв линии.

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

Часто данные приходят в «плоском» формате API и требуют преобразования.

Исходный формат:

const raw = [
  { type: "A", month: "янв", value: 10 },
  { type: "A", month: "фев", value: 20 },
  { type: "B", month: "янв", value: 15 },
  { type: "B", month: "фев", value: 25 }
];

Преобразование:

const grouped = raw.reduce((acc, item) => {
  let series = acc.find(s => s.id === item.type);

  if (!series) {
    series = { id: item.type, data: [] };
    acc.push(series);
  }

  series.data.push({
    x: item.month,
    y: item.value
  });

  return acc;
}, []);

Результат соответствует структуре Nivo Line.

Настройка доступа к полям (accessors)

Nivo позволяет использовать произвольные структуры данных через xScale и yScale, но чаще используется трансформация перед передачей. Однако возможен вариант с кастомными полями:

const data = [
  {
    id: "custom",
    data: [
      { time: 1, value: 10 },
      { time: 2, value: 20 }
    ]
  }
];

И настройка:

<ResponsiveLine
  data={data}
  xScale={{ type: "linear" }}
  yScale={{ type: "linear" }}
  axisBottom={{
    legend: "time",
    legendOffset: 36
  }}
  // маппинг через preprocess:
  data={data.map(series => ({
    ...series,
    data: series.data.map(d => ({ x: d.time, y: d.value }))
  }))}
/>

Фактически библиотека ожидает строго x/y, поэтому кастомные ключи всегда приводятся к стандарту на уровне подготовки данных.

Сортировка данных

Корректный порядок точек критичен для построения линии.

Числовая ось

data.data.sort((a, b) => a.x - b.x);

Дата

data.data.sort((a, b) => new Date(a.x) - new Date(b.x));

Категории

Сортировка выполняется вручную:

const order = ["янв", "фев", "мар"];

data.data.sort((a, b) => order.indexOf(a.x) - order.indexOf(b.x));

Отсутствие сортировки приводит к «ломаным» линиям и некорректной интерполяции.

Дублирующиеся значения X

Повторяющиеся значения x внутри одной серии создают неоднозначное поведение.

data: [
  { x: 1, y: 10 },
  { x: 1, y: 15 }
]

Возможные последствия:

  • визуальное наложение точек
  • непредсказуемое поведение tooltip
  • некорректная интерполяция

Рекомендуется агрегировать данные заранее:

const map = new Map();

raw.forEach(d => {
  map.set(d.x, (map.get(d.x) || 0) + d.y);
});

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

Хотя Line chart не является stacked по умолчанию, данные могут подготавливаться для имитации накопления:

const data = [
  {
    id: "A",
    data: [
      { x: "янв", y: 10 },
      { x: "фев", y: 20 }
    ]
  },
  {
    id: "B",
    data: [
      { x: "янв", y: 5 },
      { x: "фев", y: 15 }
    ]
  }
];

Далее производится предварительное суммирование:

const stacked = data[1].data.map((d, i) => ({
  x: d.x,
  y: d.y + data[0].data[i].y
}));

Большие наборы данных

При увеличении количества точек важно учитывать формат:

  • числовой X предпочтительнее строкового
  • избегать избыточных вложенных объектов
  • минимизировать преобразования во время рендера

Оптимальный формат:

{ x: number, y: number }

Любые дополнительные поля следует выносить за пределы data или использовать на уровне preprocessing.

Вложенные метаданные

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

{
  x: "янв",
  y: 10,
  meta: {
    region: "EU",
    status: "ok"
  }
}

Nivo игнорирует meta, но передаёт его в tooltip через кастомные компоненты:

tooltip={({ point }) => (
  <div>
    {point.data.meta?.region}
  </div>
)}

Это позволяет расширять модель данных без нарушения основного формата.

Итоговые требования к структуре данных

Корректный формат Line chart в Nivo всегда сводится к следующей структуре:

  • массив серий
  • каждая серия имеет id
  • каждая серия содержит массив data
  • каждая точка имеет x и y
  • порядок точек должен быть детерминированным
  • тип x должен соответствовать выбранной шкале

Любые отклонения от этой модели требуют явной нормализации до передачи в компонент.