Степенной масштаб: d3.scalePow() и d3.scaleSqrt()

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

Библиотека D3 предоставляет два основных инструмента для работы со степенными преобразованиями:

  • d3.scalePow() — универсальная степенная шкала с настраиваемым показателем степени
  • d3.scaleSqrt() — частный случай степенной шкалы с фиксированным показателем 0.5

Математическая основа степенного масштабирования

Степенная шкала реализует преобразование вида:

genui{“math_block_widget_always_prefetch_v2”:{“content”:“y = x^k”}}

где:

  • x — исходное значение из домена
  • y — преобразованное значение в диапазоне
  • k — показатель степени (exponent)

При:

  • k = 1 получается линейная шкала
  • k < 1 происходит сжатие больших значений
  • k > 1 усиливается разница между большими значениями

Особый случай:

y = x^{0.5} =


d3.scalePow(): общая степенная шкала

Базовая структура

const scale = d3.scalePow()
  .exponent(1.5)
  .domain([0, 100])
  .range([0, 500]);

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

domain([min, max]) Определяет входной диапазон данных. Значения за пределами домена могут быть интерполированы или обрезаны при включённом clamp.

range([min, max]) Определяет выходной диапазон визуализации (например, пиксели на экране).

exponent(k) Задаёт степень преобразования:

d3.scalePow().exponent(0.5)
d3.scalePow().exponent(2)
d3.scalePow().exponent(3)

Поведение степенной шкалы

При использовании scalePow() преобразование происходит по следующей логике:

  1. Нормализация входного значения:

    • приведение x к диапазону [0, 1]
  2. Применение степени:

    • возведение нормализованного значения в степень k
  3. Линейное масштабирование в range


Пример: усиление малых значений

const scale = d3.scalePow()
  .exponent(0.5)
  .domain([0, 100])
  .range([0, 300]);

console.log(scale(25));  // ближе к началу диапазона
console.log(scale(100)); // 300

При exponent < 1 малые значения визуально растягиваются, а большие сжимаются.


Пример: усиление различий крупных значений

const scale = d3.scalePow()
  .exponent(2)
  .domain([0, 10])
  .range([0, 500]);

console.log(scale(2));  // небольшое значение
console.log(scale(8));  // резко увеличенное значение

При exponent > 1 шкала становится чувствительной к большим значениям.


d3.scaleSqrt(): специализированная корневая шкала

d3.scaleSqrt() является упрощённой формой степенной шкалы с фиксированным показателем:

d3.scaleSqrt() === d3.scalePow().exponent(0.5)

Базовый пример

const scale = d3.scaleSqrt()
  .domain([0, 100])
  .range([0, 200]);

Особенности d3.scaleSqrt()

1. Упрощённый API

Отсутствует необходимость задавать exponent:

d3.scaleSqrt()

вместо:

d3.scalePow().exponent(0.5)

2. Визуальная интерпретация

Корневая шкала часто используется для:

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

Причина — площадь объекта растёт квадратично, и прямое линейное масштабирование искажает восприятие.


Геометрический смысл корневой шкалы

Если значение отображается как радиус круга:

genui{“math_block_widget_always_prefetch_v2”:{“content”:“A = r^2”}}

то при линейной шкале увеличение значения приводит к квадратичному росту площади. Использование квадратного корня компенсирует этот эффект:

r =

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


Сравнение scalePow и scaleSqrt

Универсальность

  • scalePow() — полностью настраиваемая степень
  • scaleSqrt() — фиксированный частный случай

Читаемость кода

d3.scaleSqrt()

выражает намерение напрямую, тогда как:

d3.scalePow().exponent(0.5)

требует интерпретации.


Гибкость

scalePow() позволяет реализовать:

  • квадратичные шкалы (exponent = 2)
  • кубические (exponent = 3)
  • дробные степени (exponent = 0.3, 1.7 и др.)

scaleSqrt() ограничен одним сценарием.


Инверсия шкалы

Обе шкалы поддерживают обратное преобразование:

const scale = d3.scaleSqrt()
  .domain([0, 100])
  .range([0, 400]);

scale.invert(200); // возвращает исходное значение

Инверсия важна при:

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

Clamping значений

scale.clamp(true);

При включённом clamp значения за пределами domain ограничиваются границами диапазона.

Это предотвращает:

  • выход графики за пределы контейнера
  • некорректные визуальные артефакты

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

Размеры узлов (force-directed graphs)

const radiusScale = d3.scaleSqrt()
  .domain([1, 1000])
  .range([2, 30]);

Применение:

  • социальные сети
  • графы связей
  • сетевые модели

Картограммы и плотности

const sizeScale = d3.scalePow()
  .exponent(0.6)
  .domain([0, 100000])
  .range([0, 50]);

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


Диаграммы рассеяния с размером маркеров

const r = d3.scaleSqrt()
  .domain([minValue, maxValue])
  .range([3, 20]);

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


Интерполяция и типы значений

Степенные шкалы поддерживают интерполяцию через встроенный механизм D3:

  • числовая интерполяция по умолчанию
  • поддержка цветов через d3.interpolateRgb (при использовании range с цветами)
  • плавное преобразование значений при анимациях

Ticks и оси

Степенные шкалы совместимы с осями D3:

const axis = d3.axisBottom(scale)
  .ticks(5);

Однако распределение тиков может быть неравномерным из-за нелинейности функции.


Отличия от линейной и логарифмической шкал

Линейная шкала

genui{“math_block_widget_always_prefetch_v2”:{“content”:“y = x”}}

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

Логарифмическая шкала

genui{“math_block_widget_always_prefetch_v2”:{“content”:“y = (x)”}}

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

Степенная шкала

genui{“math_block_widget_always_prefetch_v2”:{“content”:“y = x^k”}}

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

Практические особенности выбора exponent

  • 0.3 – 0.7: сильное сжатие больших значений
  • 0.5: визуально корректное отображение площадей
  • 1: линейное отображение
  • 1.5 – 3: усиление различий крупных значений

Поведение на краях диапазона

При значениях domain, включающих 0:

  • exponent < 1 не вызывает ошибок
  • 0^k корректно интерпретируется как 0
  • важно избегать отрицательных значений при дробных степенях, если не определена область

Производительность и стабильность

Степенные шкалы:

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

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

  • выбор exponent без учета визуальной задачи
  • применение scalePow вместо scaleSqrt для радиусов без понимания площади
  • игнорирование invert при обработке интерактивных событий
  • использование степенной шкалы там, где достаточно линейной

Композиция с другими шкалами

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

  • d3.scaleColor (цветовые интерпретации)
  • d3.scaleTime (временные ряды)
  • d3.scaleLinear (координаты)

Пример комбинированного подхода:

const xScale = d3.scaleTime().domain([...]).range([...]);
const rScale = d3.scaleSqrt().domain([...]).range([...]);
const cScale = d3.scaleSequential(d3.interpolateViridis).domain([...]);

Поведение при анимации

При использовании переходов D3:

selection.transition()
  .attr("r", d => scale(d.value));

степенная шкала обеспечивает:

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