Пороговый масштаб: d3.scaleThreshold()

Механизм порогового масштабирования в D3.js предназначен для преобразования непрерывных или произвольных числовых значений в дискретные категории на основе заданных порогов. Такая модель полезна в ситуациях, где данные требуется классифицировать по диапазонам без равномерного разбиения, а по заранее определённым границам.


Структура пороговой шкалы

Пороговая шкала определяется двумя основными компонентами:

  • domain — массив пороговых значений
  • range — массив выходных значений (категорий)

Базовая форма:

d3.scaleThreshold()
  .domain([t0, t1, t2, ...])
  .range([r0, r1, r2, r3, ...]);

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


Принцип работы

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

  • x ≤ t0 → r0
  • t0 < x ≤ t1 → r1
  • t1 < x ≤ t2 → r2
  • x > tn → r(n+1)

Таким образом формируется ступенчатая функция, где переход между значениями происходит резко, без интерполяции.


Простейший пример

const colorScale = d3.scaleThreshold()
  .domain([10, 20, 50])
  .range(["#d73027", "#fee08b", "#1a9850", "#4575b4"]);

console.log(colorScale(5));   // "#d73027"
console.log(colorScale(15));  // "#fee08b"
console.log(colorScale(35));  // "#1a9850"
console.log(colorScale(100)); // "#4575b4"

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


Логика интервалов

Пороговая шкала разбивает область определения на интервалы вида:

  • (-∞, t0]
  • (t0, t1]
  • (t1, t2]
  • (tn, +∞)

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


Отличие от других шкал D3

scaleLinear

Линейная шкала выполняет интерполяцию:

d3.scaleLinear()
  • непрерывное отображение
  • плавные переходы
  • числовой результат

scaleQuantize

d3.scaleQuantize()
  • равномерное разбиение диапазона на равные части
  • автоматически вычисляет интервалы

scaleThreshold

d3.scaleThreshold()
  • ручное задание границ
  • произвольные интервалы
  • категориальный результат

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


Практическое применение

Цветовые классификации

Типичный сценарий — визуализация интенсивности:

  • температура
  • уровень загрязнения
  • плотность населения
  • финансовые показатели
const pollutionColor = d3.scaleThreshold()
  .domain([50, 100, 150, 200])
  .range(["#2c7bb6", "#abd9e9", "#fdae61", "#d7191c", "#800026"]);

Каждый диапазон соответствует уровню опасности.


Категоризация данных

const gradeScale = d3.scaleThreshold()
  .domain([60, 75, 90])
  .range(["F", "C", "B", "A"]);

gradeScale(82); // "B"

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


Сегментация метрик

В аналитических панелях пороговые шкалы часто используются для:

  • сегментации пользователей (low / medium / high)
  • уровней активности
  • бизнес-метрик
const segment = d3.scaleThreshold()
  .domain([1000, 5000, 10000])
  .range(["small", "medium", "large", "enterprise"]);

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

Особое внимание требуется уделять значениям, равным порогам.

При domain:

[10, 20, 30]

поведение будет:

  • 10 → второй диапазон (≤ 10 попадает в первый, но зависит от реализации сравнения)
  • 20 → третий диапазон
  • 30 → четвёртый диапазон

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


Обратное отображение: invertExtent

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

const scale = d3.scaleThreshold()
  .domain([10, 20, 30])
  .range(["A", "B", "C", "D"]);

scale.invertExtent("B"); // [10, 20]

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


Использование в легендах

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

const colors = ["#fee5d9", "#fcae91", "#fb6a4a", "#de2d26"];

const scale = d3.scaleThreshold()
  .domain([25, 50, 75])
  .range(colors);

const legendItems = colors.map((color, i) => {
  const extent = scale.invertExtent(scale.range()[i]);
  return {
    color,
    min: extent[0],
    max: extent[1]
  };
});

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


Особенности работы с неравномерными порогами

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

const incomeScale = d3.scaleThreshold()
  .domain([20000, 50000, 100000, 200000])
  .range(["low", "lower-middle", "middle", "upper-middle", "high"]);

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


Оптимизация выбора порогов

Выбор domain напрямую влияет на интерпретацию данных:

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

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


Сравнение поведения при изменении domain и range

Изменение domain

.domain([10, 20])

→ создаёт 3 диапазона

Изменение range

.range(["a", "b", "c", "d"])

→ требует 3 порога

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


Взаимодействие с визуальными осями

При построении графиков пороговая шкала часто используется совместно с:

  • d3.axisBottom / d3.axisLeft
  • цветовой заливкой баров
  • сегментацией линий тренда

Например, в гистограммах:

const fill = d3.scaleThreshold()
  .domain([5, 10, 15])
  .range(["#d9f0a3", "#addd8e", "#78c679", "#238443"]);

Каждый столбец окрашивается в зависимости от высоты.


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

Любое значение меньше минимального порога или больше максимального попадает в крайние интервалы:

  • x ≤ min(domain) → первый элемент range
  • x > max(domain) → последний элемент range

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


Комбинирование с другими шкалами

Пороговая шкала может быть частью более сложных систем:

  • предварительное масштабирование через linear scale
  • затем категоризация через threshold
  • финальная стилизация через ordinal scale

Пример цепочки:

const normalize = d3.scaleLinear()
  .domain([0, 100])
  .range([0, 1]);

const classify = d3.scaleThreshold()
  .domain([0.25, 0.5, 0.75])
  .range(["low", "medium", "high", "critical"]);

Типичные ошибки при использовании

  • несортированный domain приводит к некорректным интервалам
  • mismatch длины range и domain
  • использование шкалы для непрерывной интерполяции вместо классификации
  • попытка применять threshold к нечисловым данным без предварительного преобразования

Семантическая роль пороговой шкалы

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