Выбор цветовой шкалы для данных: рекомендации и подводные камни

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


Непрерывные (sequential scales)

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

Основная идея — монотонное изменение цвета от минимального к максимальному значению.

В D3 такие шкалы обычно строятся через:

d3.scaleSequential(d3.interpolateViridis);

или через числовую доменную функцию:

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

Ключевая особенность

Цвета должны изменяться равномерно с точки зрения восприятия, а не только математически.


Дивергентные (diverging scales)

Используются, когда данные имеют центральную точку отсчёта (например, 0), и отклонения в обе стороны одинаково важны.

d3.scaleDiverging(d3.interpolateRdBu)
  .domain([-10, 0, 10]);

Поведение шкалы

  • отрицательные значения → один цветовой полюс
  • ноль → нейтральная точка
  • положительные значения → противоположный полюс

Важный аспект

Центр должен быть осмысленно выбран. Неверная установка домена делает визуализацию статистически некорректной.


Категориальные (ordinal scales)

Используются для дискретных категорий.

d3.scaleOrdinal(d3.schemeCategory10);

или

d3.scaleOrdinal()
  .domain(["A", "B", "C"])
  .range(["#1f77b4", "#ff7f0e", "#2ca02c"]);

Особенность

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


Цветовые схемы и интерполяции

Встроенные палитры D3

D3.js предоставляет набор perceptual interpolators:

  • d3.interpolateViridis
  • d3.interpolateInferno
  • d3.interpolateMagma
  • d3.interpolatePlasma
  • d3.interpolateCividis

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

Пример:

d3.scaleSequential()
  .domain([0, 1])
  .interpolator(d3.interpolateViridis);

Почему rainbow-палитры опасны

Цветовые схемы типа «радужной шкалы» (rainbow) визуально привлекательны, но приводят к искажениям:

  • неравномерное восприятие градиента
  • ложные «границы» в данных
  • потеря монотонности

Например:

d3.scaleSequential(d3.interpolateRainbow);

Такая шкала редко подходит для аналитических задач.


Перцептивная равномерность

Человеческое зрение не воспринимает RGB-пространство линейно. Поэтому линейная интерполяция в RGB приводит к:

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

Лучшие альтернативы:

  • Lab
  • HCL (Hue-Chroma-Luminance)

В D3 интерполяции уже учитывают эти особенности через специализированные функции.


Дискретизация значений: quantize, quantile, threshold

Quantize scale

Разбивает непрерывный диапазон на фиксированные интервалы:

d3.scaleQuantize()
  .domain([0, 100])
  .range(["#f7fbff", "#08306b"]);

Используется для:

  • карт плотности
  • упрощённой визуализации

Quantile scale

Разделяет данные по квантилям:

d3.scaleQuantile()
  .domain(dataArray)
  .range(colorScheme);

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

  • интервалы не равны по ширине
  • зависят от распределения данных

Threshold scale

Ручное определение границ:

d3.scaleThreshold()
  .domain([10, 20, 50])
  .range(["green", "yellow", "orange", "red"]);

Используется в:

  • классификации рисков
  • уровневых индикаторах

Категориальные палитры и проблема различимости

Категориальные шкалы должны учитывать:

  • различимость при дальтонизме
  • печать в grayscale
  • ограниченное число категорий

Проблема возникает при превышении ~10–12 категорий: цвета начинают повторяться или сливаться.

Типичный пример:

d3.scaleOrdinal(d3.schemeTableau10);

Доступность и восприятие

Цветовая слепота

Наиболее распространённые формы:

  • дейтеранопия
  • протанопия
  • тританопия (редко)

Нежелательные комбинации:

  • красный + зелёный
  • зелёный + коричневый
  • синий + фиолетовый (в некоторых оттенках)

Контрастность

При наложении цветов на фон необходимо учитывать коэффициент контраста.

Слабый контраст приводит к:

  • потере данных на графике
  • ухудшению читаемости при уменьшении масштаба

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

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

Типичные проблемы:

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

Решение:

  • логарифмическая шкала
  • ограничение домена
  • winsorization

Пример логарифмической трансформации:

d3.scaleSequential()
  .domain([1, 1000])
  .interpolator(d3.interpolateViridis)
  .clamp(true);

Работа с альфа-каналом

Прозрачность часто используется как дополнительное измерение.

Ошибки:

  • наложение прозрачных слоёв искажается при пересечении
  • фон влияет на восприятие цвета

Рекомендуется:

  • избегать сложных наложений с alpha < 0.3
  • фиксировать фон при сравнительных графиках

Темные темы и инверсия шкал

На тёмном фоне восприятие цвета меняется:

  • яркие цвета становятся агрессивнее
  • тёмные оттенки «теряются»

Типичный подход:

  • инверсия luminance
  • использование палитр, адаптированных к dark mode (например, cividis)

Сравнение непрерывных интерполяторов

Интерполятор Особенность
Viridis равномерная яркость
Inferno высокая контрастность
Plasma яркие насыщенные цвета
Magma тёплая гамма
Cividis оптимизирован для дальтонизма

Практические ловушки при выборе цветовой шкалы

1. Подмена смысла цветом

Цвет начинает восприниматься как отдельная переменная, а не отображение данных.

2. Перегрузка категорий

Слишком большое количество цветов разрушает структуру визуализации.

3. Игнорирование распределения данных

Равномерная шкала при сильно скошенных данных создаёт ложную интерпретацию.

4. Использование неподходящих палитр

Rainbow и случайные наборы цветов создают визуальный шум вместо информации.

5. Несоответствие шкалы типу данных

  • ordinal шкала для чисел → потеря порядка
  • sequential шкала для категорий → ложная иерархия

Интерполяция и контроль яркости

При создании пользовательских шкал важно контролировать не только оттенок, но и luminance:

d3.scaleSequential()
  .domain([0, 1])
  .interpolator(t => d3.interpolateLab("white", "steelblue")(t));

Lab-интерполяция обеспечивает более корректное восприятие переходов.


Смысловое соответствие цвета данным

Цветовая шкала должна соответствовать семантике:

  • температура → холодные/тёплые цвета
  • отклонения → противоположные цвета
  • интенсивность → монотонный градиент
  • категории → различимые оттенки без иерархии

Нарушение семантики приводит к когнитивному диссонансу при интерпретации визуализации.


Масштабирование и калибровка

При изменении масштаба графика важно сохранять:

  • постоянство цветового распределения
  • независимость цвета от плотности пикселей
  • стабильность интерпретации при ресайзе

В D3 это достигается фиксацией домена и пересчётом шкалы только при изменении данных, а не отображения.