Кольцевые столбчатые диаграммы в ассортименте

  • Tutorial

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


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


Круговая столбчатая диаграмма


Кольцевая, круговоая, или радиальная столбчатая диаграмма (radial column/bar chart) является вариацией на тему классической столбчатой диаграммы (bar chart).
Окружность в качестве оси абсцисс (X) и концентрические окружности в роли координатной решетки, вот собственно отличительные особенности это вида графика от обычной столбчатой диаграммы. Выглядит это всё следующим образом.
Radial Column Chart


Тут есть один нюанс, а именно какую шкалу (scale) использовать? Дело в том, что чем дальше от центра, тем больше будет длина внешней дуги (C = 2πR) и площадь нашего сектора (S ~ πR2). Поэтому нам нужно решить что для нас важнее: компенсировать различный визуальный вес наших столбцов или сохранить регулярность сетки.


Для первого варианта можно использовать масштаб, предложенный Майком Бостоком:


(function(global, factory) {
  typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("d3-scale")) :
  typeof define === "function" && define.amd ? define(["exports", "d3-scale"], factory) :
  (factory(global.d3 = global.d3 || {}, global.d3));
}(this, function(exports, d3Scale) {
  'use strict';

  function square(x) {
    return x * x;
  }

  function radial() {
    var linear = d3Scale.scaleLinear();

    function scale(x) {
      return Math.sqrt(linear(x));
    }

    scale.domain = function(_) {
      return arguments.length ? (linear.domain(_), scale) : linear.domain();
    };

    scale.nice = function(count) {
      return (linear.nice(count), scale);
    };

    scale.range = function(_) {
      return arguments.length ? (linear.range(_.map(square)), scale) : linear.range().map(Math.sqrt);
    };

    scale.ticks = linear.ticks;
    scale.tickFormat = linear.tickFormat;

    return scale;
  }

  exports.scaleRadial = radial;

  Object.defineProperty(exports, '__esModule', {value: true});
}));

Для регулярной сетки используйте стандартный линейный масштаб (d3.scaleLinear()). Я в своих примерах использовал именно его.


Постановка задачи


Предыстория задачи

Некоторое время назад мне довелось пообщаться с представителями одного из подразделений Департамента транспорта Москвы, они то мне и предложили подумать над задачей визуализации данных по ДТП на МКАД. Предполагалось, что есть данные о месте и времени происшествия и скоростном режиме на участке дороги. Нужно было проверить есть ли связь между скоростным режимом и количеством аварий. Найти пространственные и временные паттерны (если они существуют). Первая задача решается с помощью диаграммы рассеяния (scatterplot) и построения регрессии. Вторая куда более креативная, ей то мы здесь и будем заниматься.
Да, никаких данных мне так и не предоставили, так что дальше пары эскизов и обсуждений дело не пошло.


Здесь и далее будем отображать «гипотетическое» (рандомное) количество аварий на МКАД за отчётный период. Ось абсцисс разбита на километровые отсечки и соответствующим образом повёрнута. Основная цель визуализации найти потенциальные пространственные и временные аномалии или паттерны. Да, нужно сохранить «топологичность» дороги, то есть закольцованность. Это красиво, и в данном случае вполне логично.


Две переменные одним махом


Поскольку движение на МКАД у нас двустороннее, то и статистику нам нужно отображать сразу по двум направлениям. Сделать это можно разными способами, рассмотрим их подробнее. Все графики кликабельны и ведут к исходникам на bl.ocks.org. Каждый вариант диграммы сопровождается комментарием о её читабельности*. Некоторые недостатки графиков можно нивелировать с помощью интерактива: различные ховер эффекты, фокусировки и др… Но мы здесь будем рассматривать возможности графиков только как статичных картинок.


* читабельность графика — совокупность свойств графика, определяющих скорость и полноту восприятия информации, отображённой на графике.


Круговая столбчатая диаграмма с группировкой


Пожалуй первое, что приходит в голову, это сгруппировать показатели.
Radial Groupped Bar Chart


Читабельность:
Оценить картину в целом довольно сложно, из-за близости столбцов их легко сравнивать, но сложно оценить два тренда разом.


Две круговые столбчатые диаграммы


Попробуем разнести наши дороги и данные в пространстве.


Radial Double Bar Chart


Читабельность:
Здесь просто два графика. Тренды легко отслеживаются. Попарное сравнение тоже работает, но хуже. Так бывает, одно лечим, другое калечим.


Круговая накопительная столбчатая диаграмма


Снова попробуем объединить показтели, теперь в виде накопительной диаграммы.
Radial Stacked Bar Chart


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


Читабельность:
Попарное сравнение есть, суммарное значение есть (хоть и не нужно), а вот отследить тренд для второго показателя весьма сложно из-за разного начального уровня.


Круговая накопительная расходящаяся столбчатая диаграмма


Вариант накопительный диаграммы с отрицательными значениями. У нас их нет, но нам важно, что база у столбцов здесь общая. Меняем знак у одной из колонок, затем создаём накопительную диаграмму со смещением: stack.offset([offset]). В качестве функции смещения передаём d3.stackOffsetDiverging.
Radial Diverging Stacked Bar Chart


Читабельность:
Попарное сравнение есть. Внешний тренд просматривается, внутренний тоже, но хуже. Картина в целом теперь есть, хотя просматривается ещё не чётко.


Круговая накопительная оппозитная столбчатая диаграмма


Название авторское, как собственно и график (возможно я не первопроходец, а просто плохо искал). Здесь я решил как бы инвертировать предыдущий вариант графика. Получилось вот что.
Radial Opposing Stacked Bar Chart


Данный график основан на принципах гештальта.
На принципе замыкания: наш глаз пытается замкнуть «открытые» фигуры.


Gestalt: Closure


На принципе фон-фигура.


Gestalt: figure-ground


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


С точки зрения кода оснонвое отличие в собственной функции сдвига, в данном случае она выглядит так:


function stackOffsetOpposing(series, order) {
  // Check if no staks (amount of series < 1)
  if (!((n = series.length) > 1)) return;
  // find max sum
  var stackSums = [];
  var stackMaxes = [];
  for (var i = 0, n = series.length; i < n; i++) {
    var stackMax = d3.max(series[i], function(d) { return Math.abs(d[1] - d[0])});
    stackMaxes.push(stackMax);
  }
  var max = d3.sum(stackMaxes);
  // Redifining baselines
  for (var i, j = 0, d, dy, yp, yn, n, m = series[order[0]].length; j < m; ++j) {
    for (yp = 0, yn = max, i = 0; i < n; ++i) {
      if ((dy = (d = series[order[i]][j])[1] - d[0]) >= 0) {
        //d[0] -bottom; d[1] -top
        d[0] = yp, d[1] = yp += dy;
      } else if (dy < 0) {
        d[1] = yn;
        yn += dy;
        d[0] = yn;
      } else {
        d[0] = yp;
      }
    }
  }
}

Читабельность:
Попарное сравнение есть. Оба тренда просматриваются. Есть картина в целом.


Да, график остаётся накопительным, так что можно добавлять подкатегории.
Radial Opposing Stacked Bar Chart
Преимущества:


  • хорошо видны данные в противофазе
  • нет необходимости далеко переводить взгляд, не теряется контекст
  • видно оба тренда
  • суммарная картина по «пустому» графику

Временные кольца


По аналогии с годовыми кольцами деревьев можно добавлять кольца с данными за разные отчётные периоды. Главное не переусердствовать и правильно подобрать ширину колец. Выглядеть это может так.
Double Radial Opposing Stacked Bar Chart


Неплохой вариант для печати в большом формате, на экране, по-моему, смотрится довольно-таки тяжеловесно.


Заключение


Мы рассмотрели различные варианты круговых диаграмм, их плюсы и минусы. Несмотря на то, что этот класс диаграмм далеко не самый лучший в плане читаемости, он хорош в плане визуальной привлекательности. Так что если привлечь внимание «смотрящего» для вас важнее, чем скорость выуживания полезной информации, то такой вариант может вполне подойти. Надеюсь, рассмотренные примеры будут вам полезны в реальной работе. Да, JS код не соответствует современным стандартам, но он должен быть понятен. В любом случае это лишь примеры и заготовки для реальных визуализаций.


И на последок


Если бы были реальные данные, то можно было бы добавить интерактив, прикрутить фильтрацию и т.д… А поскольку их нет, да и статья обучающая, остановимся на этом. Но если вдруг кто-нибудь (ЦОДД, Яндекс?) предоставит сэмпл с реальными данными, то можно было бы продолжить изыскания.


А какой тип график предложили бы вы? Собственные варианты пишите в комментариях =)

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

Какой тип графика кажется вам наиболее подходящим для подобной задачи?
  • +20
  • 3,6k
  • 9
Поделиться публикацией
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 9
  • +2
    Для данных, у которых ось абсцисс — время суток такой вариант графика подошел бы.
    «Зависимость загрузки сервиса в зависимости от времени», итп.

    • 0
      Да, спасибо за идею! В этом случае можно обойтись привычным циферблатом на 12 часов, а не растягивать 24 часа по окружности, как делают обычно.
      • 0
        Не факт, что двухвитковую диаграмму будет удобно воспринимать.
        • 0
          Согласен, но это нужно проверять экспериментальным путём. Провести usability тестирование c eye tracker'ом, собрать данные и проверить статистическими методами.
        • 0
          Ну, 13:05 и 01:05 — разные точки данных все-таки. Вы же не предлагаете их просуммировать?
          • 0
            В случае «загрузки сервиса» суммировать не нужно конечно.

            Но сама привязка к циферблату часов выглядит заманчиво. Это может быть очень полезно, когда нужно разметить сам циферблат. Например, как в инфографике Creative Routines.
        • 0
          А по поводу пространственных графиков, я думаю неплохо бы смотрелась «роза ветров» с шагом в несколько градусов
        • 0
          Например, многослойными бубликами с кликабельными секторами можно показывать бюджет по уровням, dvinaland.ru/budget. По мгновенной информативности не очень, особенно после изменения структуры бюджетов 2 года назад, зато юзеры её минутами разглядывают и по уровням кликают.
          • 0
            Этот тип графика называется Sunburst Diagram, он для иерархичных данных. По сути это скругленный вариант Treemap.

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

          Самое читаемое