Последовательный масштаб: d3.scaleSequential()

Последовательные шкалы в D3.js предназначены для преобразования числового непрерывного диапазона значений в непрерывный диапазон цветов или других визуальных параметров через интерполяцию. В отличие от линейных шкал, которые возвращают дискретно вычисляемые значения диапазона, последовательные шкалы используют функцию интерполяции, обеспечивая плавный переход между значениями без разрывов.

Основой последовательной шкалы является отображение вида:

x ∈ [a, b] → t ∈ [0, 1] → color = f(t)

где промежуточное значение t нормализуется, а затем передаётся в интерполятор, формирующий итоговое значение.


Базовая структура d3.scaleSequential()

Функция d3.scaleSequential() создаёт шкалу, которая принимает интерполятор и возвращает функцию масштабирования.

const scale = d3.scaleSequential(d3.interpolateViridis)
  .domain([0, 100]);

В этом примере:

  • диапазон входных значений задаётся через .domain([0, 100])
  • интерполятор d3.interpolateViridis определяет цветовую шкалу
  • результатом работы шкалы будет плавное преобразование числа в цвет

При вызове:

scale(0);   // начало диапазона
scale(50);  // середина
scale(100); // конец диапазона

возвращаются значения, находящиеся на непрерывной цветовой градиентной шкале.


Принцип работы интерполяции

Последовательная шкала не использует классический range. Вместо этого она полагается на интерполятор, который принимает нормализованное значение t:

t = (x - domainMin) / (domainMax - domainMin)

Затем:

output = interpolator(t)

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


Отличие от линейной шкалы d3.scaleLinear()

Линейная шкала возвращает значения из фиксированного массива диапазона:

d3.scaleLinear()
  .domain([0, 100])
  .range(["blue", "red"]);

Последовательная шкала же работает иначе:

d3.scaleSequential(d3.interpolateBlues)
  .domain([0, 100]);

Ключевые различия:

  • линейная шкала использует range
  • последовательная шкала использует interpolator
  • линейная шкала может возвращать дискретные значения
  • последовательная шкала всегда даёт непрерывный градиент

Интерполяторы D3.js

Основная сила d3.scaleSequential() заключается в использовании встроенных интерполяторов.

Цветовые интерполяторы

Наиболее часто используются:

  • d3.interpolateViridis
  • d3.interpolateInferno
  • d3.interpolateMagma
  • d3.interpolatePlasma
  • d3.interpolateCividis
  • d3.interpolateBlues
  • d3.interpolateGreens
  • d3.interpolateReds

Пример:

const colorScale = d3.scaleSequential(d3.interpolatePlasma)
  .domain([0, 1]);

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


Числовые и структурные интерполяторы

Интерполяция возможна не только для цветов:

d3.interpolateNumber(0, 100)(0.5); // 50

Также поддерживаются:

  • d3.interpolateRgb
  • d3.interpolateHsl
  • d3.interpolateLab
  • d3.interpolateObject

Последний вариант позволяет интерполировать сложные структуры:

d3.interpolateObject(
  {x: 0, y: 0},
  {x: 10, y: 20}
)(0.5); // {x: 5, y: 10}

Настройка домена

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

const scale = d3.scaleSequential(d3.interpolateViridis)
  .domain([minValue, maxValue]);

Если значения выходят за пределы диапазона, они автоматически интерполируются за его границами, если не включено ограничение:

scale.clamp(true);

В этом случае значения фиксируются на границах домена.


Масштабирование и нормализация

Математически процесс можно представить как композицию функций:

scale(x) = interpolator( normalize(x) )

где:

normalize(x) = (x - min) / (max - min)

Это делает последовательные шкалы особенно удобными для визуализаций, где требуется плавное изменение цвета или интенсивности.


Использование в визуализации данных

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

  • тепловых картах (heatmaps)
  • картограммах (choropleth maps)
  • визуализации плотности данных
  • отображении градиентов значений в scatter plots
  • визуализации временных рядов через цвет

Пример применения к SVG-элементам:

const color = d3.scaleSequential(d3.interpolateViridis)
  .domain([0, 100]);

d3.selectAll("circle")
  .attr("fill", d => color(d.value));

Обратное масштабирование

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

scale.invert(value)

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


Нормализация цветового восприятия

Одно из ключевых преимуществ последовательных шкал — перцептивная равномерность. Некоторые интерполяторы, такие как Viridis, разработаны так, чтобы:

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

Вариации последовательных шкал

В D3 существуют специализированные версии последовательных шкал:

d3.scaleSequentialLog()

Используется для логарифмически распределённых данных:

d3.scaleSequentialLog(d3.interpolatePlasma)

d3.scaleSequentialPow()

Позволяет задавать степенную трансформацию:

d3.scaleSequentialPow(d3.interpolateViridis)
  .exponent(2);

d3.scaleSequentialSqrt()

Частный случай степенной функции с экспонентой 0.5:

d3.scaleSequentialSqrt(d3.interpolateInferno)

Эти варианты полезны при работе с неравномерно распределёнными данными, где линейное отображение искажает восприятие.


Работа с пользовательскими интерполяторами

Последовательная шкала может использовать пользовательскую функцию:

const customInterpolator = t => `rgba(${255 * t}, 0, ${255 * (1 - t)}, 1)`;

const scale = d3.scaleSequential(customInterpolator)
  .domain([0, 100]);

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


Комбинация с особыми доменами данных

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

  • нормализация выбросов
  • обрезка экстремальных значений
  • применение квантилей перед передачей в шкалу

Пример:

const extent = d3.extent(data, d => d.value);

const color = d3.scaleSequential(d3.interpolateViridis)
  .domain(extent);

Поведение при выходе за границы

Если значение выходит за пределы домена, поведение зависит от настройки:

  • без clamp: интерполятор может экстраполировать значения
  • с clamp(true): значение фиксируется на ближайшей границе
scale.clamp(true);

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


Применение в многоуровневых визуальных системах

Последовательные шкалы часто выступают базовым компонентом более сложных систем визуализации:

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

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