Содержание


Визуализация данных

Часть 1. Визуализация метрик посещения с помощью графики SVG и D3

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

Серия контента:

Этот контент является частью # из серии # статей: Визуализация данных

Следите за выходом новых статей этой серии.

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

Следите за выходом новых статей этой серии.

В этой статье, первой из двух статей серии, мы демонстрируем методы визуализации, которые помогают извлекать коммерческую информацию из имеющихся данных. Вы научитесь применять масштабируемую векторную графику (SVG) и библиотеку с открытым исходным кодом D3 JavaScript для создания просматриваемых в браузере визуализаций, которые преобразуют информацию в фигуры и цвета. Я покажу вам методы с примерами, которые визуализируют метрики посещения, относящиеся к использованию социальных ресурсов. В первой части я представляю обзор совместного применения SVG и D3, а также привожу несколько простых примеров. Часть 2 посвящена более углубленному описанию возможностей визуализации, которые предоставляют эти мощные технологии с открытым исходным кодом.

Анализ метрики социальных ресурсов

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

Предположим, что есть некая занимающаяся оформлением интерьеров компания, которая публикует открытый контент в виде блогов, видео, страниц Facebook и дискуссионных форумов. Этот контент представляет идеи компании через социальные ресурсы и призван инициировать дискуссии и другие формы общения. Контент ориентируется на вкусы и предпочтения отдельных клиентов и соответственно помогает им переходить с одного социального ресурса на другой. Чтобы оценить изменяющиеся тенденции и предложить новые подходы и новые разработки, компания должна проанализировать данные о посещениях по трём направлениям:

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

В таблицах 1, 2 и 3 приведены выборочные данные, показывающие число посещений, взаимодействие пользователей и переходы за последние три недели. Обратите внимание, что для представления типов социальных ресурсов (таких как блоги или страницы Facebook) в таблицах используются цвета.

В таблице 1 приведено число посещений каждого ресурса:

Таблица 1. Таблица 1. Число посещений каждого социального ресурса
Социальный ресурсСинийЖелтыйЗеленыйКрасныйБордовый
Неделя 170577483374938464598
Неделя 223717397458928618249
Неделя 359725672915297258983

Из таблицы 1 видно, что синий ресурс посещался в 1-ю неделю 7057 раз, а самым посещаемым в эту неделю был жёлтый ресурс.

В таблице 2 приведены данные о взаимодействии:

Таблица 2. Таблица 2. Число взаимодействующих пользователей в каждом социальном ресурсе
Социальный ресурсСинийЖелтыйЗеленыйКрасныйБордовый
Неделя 120522089158614262632
Неделя 220712190721437822721
Неделя 330763190453238254831

Из таблицы 2 видно, что в 1-ю неделю в синем ресурсе общались 2052 пользователя, а максимальный уровень общения в первую неделю был в бордовом ресурсе.

В таблице 3 показано число пользователей, перешедших с синего ресурса на другие ресурсы:

Таблица 3. Таблица 3. Данные о переходе пользователей с синего социального ресурса
Социальный ресурсС синего на жёлтыйС синего на зелёныйС синего на красныйС синего на бордовый
Неделя 13057348387498456
Неделя 22371739745892861
Неделя 35972567291529725

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

Визуализация данных посещения

Визуальное представление позволяет проще и быстрее интерпретировать большие объёмы данных, чем числовые таблицы. Данные, содержащиеся в таблицах 1, 2 и 3, можно представить графически несколькими способами. Например, на рисунке 1 показан простейший способ отображения данных таблицы 1 за 1-ю неделю:

Рисунок 1. Число посещений социальных ресурсов за 1-ю неделю, представленное кругами
Number of views
Number of views

На рисунке 1 изображено число посещений каждого ресурса в виде кругов. Относительные размеры кругов пропорциональны представляемому числу, поэтому самый посещаемый ресурс (жёлтый) представлен самым большим кругом. Кроме того, каждый круг показывает реальное число посещений за 1-ю неделю.

На рисунке 2 показан другой вариант рисунка 1, использующий иную компоновку кругов:

Рисунок 2. Посещения за 1-ю неделю, представленные иным расположением кругов
Same circles as in Figure 1 but laid out slightly in an arc rather than horizontally.
Same circles as in Figure 1 but laid out slightly in an arc rather than horizontally.

Популярность каждого ресурса — это лишь одна из мер данных, которые хочет проанализировать компания. На рисунке 3 показано число посещений каждого ресурса и число взаимодействующих пользователей на этом ресурсе за 1-ю неделю — два измерения в одном визуальном представлении:

Рисунок 3. Круги внутри кругов, показывающие число посещений и число взаимодействующих пользователей за 1-ю неделю
Visualization consisting of circles within circles     showing the number of user views and the number of users interacting on each resource
Visualization consisting of circles within circles showing the number of user views and the number of users interacting on each resource

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

Рисунки 1, 2 и 3 демонстрируют простой способ комбинирования цветов, форм и чисел для представления данных. Взглянув на рисунок 3, вы увидите, что жёлтый ресурс за 1-ю неделю привлёк максимальное число посетителей, а на бордовом ресурсе было самое интенсивное взаимодействие.

Применение открытых технологий для визуализации данных

Современные открытые стандарты и инструменты с открытым исходным кодом обладают достаточной мощностью для графического представления данных. Разработанный консорциумом World Wide Web Consortium (W3C) открытый стандарт SVG определяет основанный на XML формат для построения двумерных графических объектов. (Ссылку на вводную статью об SVG можно найти в разделе Ресурсы) SVG поддерживается несколькими браузерами. Я проверил весь приведённый в этой статье код SVG и JavaScript в браузере Google Chrome.

Если вы работаете с данными и SVG, библиотека D3 сделает вас немного волшебником. Она берёт ваши данные и инструкции по рисованию, привязывает данные к необходимым тегам SVG и сразу же генерирует код SVG, который можно просмотреть в браузере. Рисуя сложные графические объекты, D3 всегда держит ваши данные под рукой, поэтому вы можете шаг за шагом всё глубже и глубже погружаться в свой рисунок и работать только с теми данными, которые нужны для рисования небольшой части вашего рисунка.

В примерах, приведенных в этой статье и в части 2, я перехожу от простых рисунков к сложным, исследуя возможности D3 и приводя код SVG и JavaScript для каждого рисунка. Я начну с самого простого случая: кода SVG, который генерирует круги, показанные на рисунке 1.

Представление данных в SVG

В листинге 1 приведен код SVG, рисующий круги на рисунке 1:

Листинг 1. Листинг 1. Код SVG, рисующий круги на рисунке 1
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
 width="1000" height="1000">

 <g transform="translate(70,143)">
  <circle r="70.57" style="fill: #000fff;"></circle>
  <text x="-10" fill="grey">7057</text>
 </g>

 <g transform="translate(216,143)">
  <circle r="74.83" style="fill: #fff555;"></circle>
  <text x="-10" fill="grey">7483</text>
 </g>

 <g transform="translate(329,143)">
  <circle r="37.49" style="fill: #aaf000;">
  </circle>
  <text x="-10" fill="grey">3749</text>
 </g>


 <g transform="translate(404,143)">
  <circle r="38.46" style="fill: #cc0000;"></circle>
  <text x="-10" fill="grey">3846</text>
 </g>

 <g transform="translate(489,143)">
 <circle r="45.98" style="fill: #993344;"></circle>
 <text x="-10" fill="grey">4598</text>
 </g>

</svg>

Как видите, корневым тегом в листинге 1 является тег <svg>. Тег <svg> создаёт холст, на котором мы будем рисовать с помощью других тегов SVG, и служит упаковкой для этих тегов. Атрибуты width (ширина) и height (высота) тега <svg> определяют размеры холста SVG. Холст, выбранный мною в листинге 1, имеет размеры 1000 на 1000 единиц.

Корневой тег <svg> содержит пять дочерних тегов <g>, по одному на каждый круг рисунка 1. Тег <g> служит упаковкой для рисования, которое выполняется для создания каждого круга и сопроводительного текста. Каждый тег <g> имеет один атрибут transform, значением которого является translate (X, Y). Значение translate (X, Y) определяет точки, вокруг которых строится круг. Например, атрибут transform первого тега <g> в листинге 1 помещает центр первого круга в позицию 70, 143.

Каждый из пяти тегов <g> имеет разное значение X атрибута transform и все эти теги имеют равное значение Y. В результате пять кругов выстраиваются вдоль горизонтальной линии. Разные значения Х тегов <g> приводят к тому, что каждый круг рисуется вслед за предыдущим без видимого промежутка. Очень скоро я покажу вам пример кода JavaScript для генерации этих значений.

Продолжая рассмотрение листинга 1, обратите внимание, что каждый тег <g> содержит два дочерних тега —<circle> и <text>— которые соответственно рисуют круг и выводят сопроводительный текст. Каждый тег <circle> имеет атрибут r, который определяет радиус круга. Заметьте, что значение атрибута r каждого тега <circle> равно числу посещений, делённому на 100. Кроме того, каждый тег <circle> имеет атрибут style, который определяет цвет круга. Для представления цветовых RGB-кодов я использую 6-разрядный шестнадцатеричный формат. (Подробную информацию о применении цветов можно найти в разделеРесурсы). Тег <text> обрамляет текст, отображаемый в каждом из кругов. Атрибут x="-10", который я включил в каждый тег <text>, немного сдвигает текст для улучшения позиционирования. Атрибут fill тега <text> определяет цвет текста.

Вы можете загрузить все примеры кода для этой статьи и открыть каждый из них в окне браузера. Попробуйте добавить ещё круги или текст в код SVG в листинге 1 и посмотрите, как они отобразятся в браузере.

Применение JavaScript и D3 для генерации кода SVG

Код JavaScript в листинге 2 использует D3 для генерации кода SVG, показанного в листинге 1:

Листинг 2. Листинг 2. Применение D3 для генерации кода SVG для рисунка 1
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v3.min.js"></script>

<script>

//шаг 1:
var views = [
  [7057, 7483, 3749, 3846, 4598],
  [ 2371, 7397, 4589, 2861, 8249],
  [ 5972, 5672, 9152, 9725, 8983],
            ];

var width = 1000, height = 1000;
var colors = [  "blue",
                "yellow", 
                "green", 
                "red", 
                "maroon"
             ];
var week = 0;

//Шаг 2:
var svg = d3.select("body").append("svg")
   .attr("width", width)
   .attr("height", height)

//Шаг 3:
   .selectAll("g")
   .data(views[week])
   .enter();

//шаг 4:
var g = svg.append("g")

//Шаг 5:
    .attr("transform", function(d,i){
  var x = 0;
  for (var count=0;
           count<i;
   	        count++   )
             x+=views[week][count];
  for (var count=0;
           count<=i;
           count++   )
             x+=views[week][count];
  return "translate(" + x/100 + "," + height / 7 + ")" }
         );

//Шаг 6:
g.append("circle")
  .attr("r", function(d){return d/100})
  .style("fill", function(d, i){return colors[i];});

g.append("text")
  .attr("x", -10)
  .attr("fill", "grey")
  .text(function(d){return d});

</script>
</body>

Я добавил в листинг 2 комментарии, чтобы обозначить отдельные шаги:

Шаг 1: на первом шаге я сохраняю данные о посещениях в массиве. Кроме того, я задаю переменные для сохранения размеров холста и цветов, которые хочу использовать в рисунках SVG.

Шаг 2: я начинаю использовать D3 с d3.select("body").append("svg"), который добавляет тег <svg> в тело HTML-страницы. Кроме того, я задаю здесь ширину и высоту холста SVG, вызывая функцию .attr().

Шаг 3: три вызова функций .selectAll().data().enter() демонстрируют силу и простоту применения D3:

  • функция .selectAll("g") выбирает все дочерние теги <g>корневого тега SVG; хотя сейчас она их выбрать не может (потому что они не существуют), она знает, что требуется сделать;
  • функция .data(views[week]) передаёт в D3 данные о посещении всех пяти ресурсов за одну неделю;
  • функция .enter() привязывает отдельные теги <g> к отдельным записям о посещении.

Шаг 4: функция .append("g") говорит D3 о том, что нужно добавить достаточное число тегов <g>— по одному тегу <g> для каждой записи данных. Поскольку сначала ни одного тега <g> не существовало, и каждая неделя в массиве данных представлена пятью записями (по одной на каждый социальный ресурс), D3 добавляет пять тегов <g> к корневому тегу <svg>. Первый тег <g> внутренне связан с первой записью данных (т.е. со значением 7057 в массиве посещений), второй тег <g> связан со второй записью и т.д. D3 внутренне связывает данные с тегами SVG, поэтому мне нужно передать массив данных лишь один раз, и дальше можно не беспокоиться о привязке данных к каждому отдельному тегу SVG.

Шаг 5: кроме того, я должен добавить атрибут transform к каждому из пяти тегов <g>, поэтому я вызываю функцию .attr(). Мне достаточно вызвать её один раз, и D3 внутренне обработает все циклы, гарантируя добавление атрибута transform в каждый из пяти тегов <g>.

Функция .attr() использует в качестве первого параметра имя атрибута (в данном случае, transform). Возвращаясь к обсуждению листинга 1, вспомните, что атрибут transform определяет положение круга, поэтому его значение должно рассчитываться функцией. Таким образом, второй параметр функции .attr() представляет собой другую функцию, которая, в свою очередь, принимает два параметра: d и i.

D3 предоставляет такую возможность вызова функции, поскольку для вычисления значения атрибута вам могут потребоваться данные, связанные с тегом <g>. Параметр d содержит данные для конкретного тега <g>, для которого вы создаёте значение атрибута transform (например, 7057 для первого тега <g>, который привязан к записи, представляющей посещение синего социального ресурса). Магия D3 заключается в том, что он внутренне устанавливает корректные ассоциации данных с отдельными тегами, поэтому вам не нужно беспокоиться о выборе требуемых данных. Вы можете сосредоточиться на создании своего SVG.

Параметр i представляет собой отсчитываемый от нуля индекс данных (например, i = 0 для первого тега <g>, который связан с первой записью). Как видите, я использую параметр i в первом шаге для расчёта соответствующего положения тега <g>, тем самым рисуя круг первого (синего) социального ресурса в крайней левой позиции, затем второй круг рядом с первым и т.д.

Если вы попробуете выполнить код только для шагов 1–5, вы увидите пять дочерних тегов <g> с атрибутами transform, как показано в коде SVG в листинге 3. (Эти теги <g> пока ещё не будут выводить рисунок в окно браузера).

Листинг 3. Листинг 3. Код SVG с первыми пятью тегами <g>
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
 width="1000" height="1000">

 <g transform="translate(70,143)">
 </g>

 <g transform="translate(216,143)">
 </g>

 <g transform="translate(329,143)">
 </g>

 <g transform="translate(404,143)">
 </g>

 <g transform="translate(489,143)">
 </g>

</svg>

Шаг 6: теперь в каждый из пяти тегов <g> нужно добавить тег <circle> и тег <text>. Для этого мы дважды вызываем функцию .append() (один раз для тега <circle> и второй раз для тега <text> ).

Обратите внимание, что для создания атрибута (radius) я использую .attr("r", function(d){return d/100}). В данном случае для построения круга пропорционального размера мне нужно знать лишь число посещений конкретного ресурса. Поэтому мне необходим только параметр d, а параметр i я опускаю. Параметр d автоматически содержит требуемые данные (число просмотров), поэтому я делю его на 100, чтобы получить размер круга.

Код JavaScript на основе D3 готов к рисованию кругов, представленных на рисунке 1. Если вы откроете Listing2.html из загруженного кода примера в окне браузера, то увидите, что он создает код SVG, показанный в листинге 1, и строит круги, представленные на рисунке 1.

Другое расположение кругов

Вы можете немного изменить код листинга 2, чтобы расположить круги, как показано на рисунке 2. Измененный JavaScript приведен в листинге 4:

Листинг 4. Листинг 4. Измененный код для построения кругов в соответствии с рисунком 2
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v3.min.js"></script>

<script>

var views = [
  [7057, 7483, 3749, 3846, 4598],
  [ 2371, 7397, 4589, 2861, 8249],
  [ 5972, 5672, 9152, 9725, 8983],
  [ 9763,  8462,  9782, 1953, 5182],
  [ 9567,  1571,  2895, 2783, 1874],
  [ 2371, 7397, 4589, 2861, 8249]
		];

var width = 1000, height = 1000;
var colors = [	"#0000ff", 	//blue
			"#ffd700", 	//gold 
			"#008000", 	//green 
			"#ff0000", 	//red 
			"#800000" 	//maroon
		];
var week = 0, scale = 100;

var svg = d3.select("body").append("svg")
   .attr("width", width)
   .attr("height", height)
   .selectAll("g").data(views[week]).enter();

var g = svg.append("g")
    .attr("transform", function(d,i){

  var x = 10000,  y = 10000;

  if (!(i%2)){
    for (var count=0;
         count<i;
         count++)
           x+=views[week][count];
    for (var count=0;
         count<=i;
         count++)
           y+=views[week][count];
    } else {
    for (var count=0;
         count<i;
         count++)
           y+=views[week][count];
    for (var count=0;
         count<=i;
         count++)
           x+=views[week][count];
    }
    return "translate(" + x/scale + "," + y/scale + ")"}
          );

g.append("circle")
  .attr("r", function(d){return d/scale})
  .style("fill", function(d, i){return colors[i];});

g.append("text")
  .attr("x", -10)
  .attr("fill", "grey")
  .text(function(d){return d});

</script>
</body>

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

Круг внутри круга

Теперь взгляните на листинг 5, который представляет собой код SVG для рисования кругов, приведенных на рисунке 3 (круги внутри кругов, одновременно представляющие число посещений и число взаимодействий):

Листинг 5. Листинг 5. Код SVG для рисунка 3
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
 width="1000" height="1000">

  <g transform="translate(100,170.57)">
    <circle r="70.57" style="fill: #0000ff;">
    </circle>
    <circle r="20.52" style="fill: #add8e6;">
    </circle>
    <text x="-15" y="35.52" fill="white">7057</text>
    <text x="-15" y="5">2052</text>
  </g>

  <g transform="translate(245.4,170.57)">
    <circle r="74.83" style="fill: #ffd700;">
    </circle>
    <circle r="20.89" style="fill: #ffff00;">
    </circle>
    <text x="-15" y="35.89" fill="white">7483</text>
    <text x="-15" y="5">2089</text>
  </g>

  <g transform="translate(245.4,282.89)">
    <circle r="37.49" style="fill: #008000;"></circle>
    <circle r="15.86" style="fill: #90ee90;"></circle>
    <text x="-15" y="30.86" fill="white">3749</text>
    <text x="-15" y="5">1586</text>
  </g>

  <g transform="translate(321.35,282.89)">
    <circle r="38.46" style="fill: #ff0000;"></circle>
    <circle r="14.26" style="fill: #f08080;"></circle>
    <text x="-15" y="29.26" fill="white">3846</text>
    <text x="-15" y="5">1426</text>
  </g>

  <g transform="translate(321.35,367.33)">
    <circle r="45.98" style="fill: #800000;"></circle>
    <circle r="26.32" style="fill: #cd5c5c;"></circle>
    <text x="-15" y="41.32" fill="white">4598</text>
    <text x="-15" y="5">2632</text>
  </g>

</svg>

Как видите, в каждом теге <g>листинг 5 содержит два тега <circle> и два тега <text>, тогда как листинг 1 содержал лишь одну пару тегов <circle> и <text>. Это легко понять, поскольку рисунок 3 содержит пары концентрических кругов, причем каждый круг имеет связанное с ним число.

В листинге 6 показан основанный на D3 код JavaScript для построения рисунка 3:

Листинг 6. Листинг 6. Код JavaScript для построения рисунка 3
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v3.min.js"></script>

<script>

var viewsAndInteraction = [
[ [7057, 2052], [7483, 2089], [3749, 1586],
  [3846, 1426], [4598, 2632]               ],
[ [5972, 2071], [5672, 2190], [9152, 7214],
  [9725, 3782], [8983, 2721]               ],
[ [8749, 3076], [4768, 3190], [6738, 4532],
  [9546, 3825], [6983, 4831]               ]
               ];

var width = 1000, height = 1000;
var viewColors = [  "blue",
                    "gold",
                    "green", 
                    "red", 
                    "maroon"
		];
var interactionColors = [  "lightblue",
                           "yellow", 
                           "lightgreen", 
                           "lightcoral", 
                           "indianred"
		];

var week = 0, scale = 100;

var svg = d3.select("body").append("svg")
   .attr("width", width)
   .attr("height", height)
   .selectAll("g")
   .data(viewsAndInteraction[week])
   .enter();

var g = svg.append("g")
    .attr("transform", function(d,i){

  var x = 10000,  y = 10000;

  if (!(i%2)){
    for (var count=0;
         count<i;
         count++)
           x+=viewsAndInteraction[week][count][0];
    for (var count=0;
         count<=i;
         count++)
           y+=viewsAndInteraction[week][count][0];
    } else {
    for (var count=0;
         count<i;
         count++)
           y+=viewsAndInteraction[week][count][0];
    for (var count=0;
         count<=i;
         count++)
           x+=viewsAndInteraction[week][count][0];
    }
    return "translate(" + x/scale + "," + y/scale + ")"}
          );

g.append("circle")
  .attr("r", function(d){return d[0]/scale})
  .style("fill", function(d, i){return viewColors[i];});

g.append("circle")
  .attr("r", function(d){return d[1]/scale})
  .style("fill", function(d, i){return interactionColors[i];});

g.append("text")
  .attr("x", -15)
  .attr("y", function(d){return (15 + d[1]/scale);})
  .attr("fill", "white")
  .text(function(d){return d[0]});

g.append("text")
  .attr("x", -15)
  .attr("y", 5)

  .text(function(d){return d[1]});

</script>
</body>

Если сравнить JavaScript в листинге 2 с кодом листинга 6, то можно увидеть, что почти ничего не изменилось. Отличия листинга 6 заключаются в следующем:

  • нам нужно работать с данными о посещениях и взаимодействиях, поэтому массив viewsAndInteraction содержит эти данные;
  • две пары функций g.append("circle") и g.append("text") вызываются для рисования двух концентрических кругов, каждый со своим текстом.

Теперь, когда вы увидели код SVG и JavaScript для всех трёх рисунков, давайте займёмся визуализацией данных о переходах.

Визуальное представление данных о переходах

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

Рисунок 4. Применение цветных дуг и хорд для отображения данных о переходах
Image of circular arcs and chords
Image of circular arcs and chords

Цветные дуги на рисунке 4 представляют разные социальные ресурсы. Синий ресурс соединяется хордами со всеми другими ресурсами. Каждая хорда соответствует переходу.

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

Обратите внимание, что хорды (начиная с синей) расположены в возрастающем порядке, так что более тонкая хорда соответствует меньшему числу пользователей. Таким образом, можно без чисел понять, какие ресурсы привлекли больше пользователей.

Применение SVG для рисования дуг и хорд

Код SVG для создания рисунка 4 показан в листинге 7:

Листинг 7. Листинг 7. Фрагмент кода SVG для создания рисунка 4
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
 width="500" height="500">

  <g transform="translate(250,250)">
    <path style="fill: #0000ff;" 
      d="M1.1633760361312584e-14,-190A190,190 0 0,1   
      177.80498922385382,-66.97302298019123L140.372
      3599135688,-52.873439194887816A150,150 0 0,0 
      9.18454765366783e-15,-150Z"></path>
    <path style="fill: #ffd700;" 
      d="M177.80498922385382,-66.97302298019123A190,190 0 0,1 
      141.9432774826573,126.30164677264251L112.06048222315049,99
      .71182639945462A150,150 0 0,0 140.3723599135688,-  
      52.873439194887816Z"></path>
    <path style="fill: #008000;"
      d="M141.9432774826573,126.30164677264251A190,190 0 0,1 -
      141.87314929322426,126.38041584684909L-     
      112.00511786307179,99.77401251067033A150,150 0 0,0 
      112.06048222315049,99.71182639945462Z"></path>
    <path style="fill: #ff0000;" 
      d="M-141.87314929322426,126.38041584684909A190,190 0 
      0,1 -136.03537960886078,-132.64379176830383L-
      107.39635232278484,-104.7187829749767A150,150 0 0,0 -
      112.00511786307179,99.77401251067033Z"></path>
    <path style="fill: #800000;" 
      d="M-136.03537960886078,-132.64379176830383A190,190 0 0,1 -
      3.7240908056998534e-13,-190L-2.9400716887104106e-13,-
      150A150,150 0 0,0 -107.39635232278484,-
      104.7187829749767Z"></path>
    <g>

      <!--Drawing SVG code for chords goes here. -->  

    </g>
  </g>
</svg>

Как видите, корневой тег <svg> в листинге 7 содержит один дочерний тег <g>, который, в свою очередь, имеет пять тегов <path> и один внутренний дочерний тег <g>. Пять тегов <path> рисуют пять цветных дуг. Внутренний тег <g> упаковывает код SVG для рисования хорд, о чём я очень скоро расскажу. Но сначала я поясню, как теги <path> рисуют пять дуг.

Рисование цветных дуг

Рисование набора дуг соответствующего цвета легко выполняется с помощью тега SVG <path>, который предназначен для определения контура рисования, аналогично тому, как это делается карандашом на бумаге. Контур может быть замкнутым (например, треугольник) или незамкнутым (например, набор соединенных между собой линий). Для рисования дуги потребуется замкнутый контур, как показано на рисунке 5:

Рисунок 5. Рисование дуги с помощью тега <path>
Drawing an arc

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

Каждая из пяти дуг на рисунке 4 имеет одинаковые цвета для периметра и для заполнения замкнутого контура. Поэтому нам понадобится пять тегов <path>, по одному на каждую дугу.

Из листинга 7 видно, что каждый из пяти тегов <path> содержит два атрибута: style и d. Атрибут style определяет цвет, используемый для рисования, а атрибут d— контур рисования.

Значения атрибутов d (например, M8.572244476756641e-15,-140A140,140 0 0,1 93.95877067680189,103.78703875197591L67.11340762628707,74.13359910855422A100,100 0 0,0 6.123031769111886e-15,-100Z) выглядят длинными и сложными. Этот формат требуется официальными спецификациями SVG для определения точного контура рисования.

Формат состоит из буквы M, за которой следуют два разделённых запятыми числа, затем идёт буква A, за которой следует несколько разделенных запятыми чисел, затем ещё одна буква A, за которой следует несколько чисел, и, наконец, буква Z. Эта последовательность означает следующее: M— переместиться в точку, определяемую числами после буквы M; затем A— нарисовать дугу; затем L— нарисовать линию к точке, определенной числами после L; затем A— нарисовать ещё одну (вторую) дугу. Буква Z в конце дает команду вернуться назад в исходное положение, тем самым формируя замкнутый контур.

Хорошая новость заключается в том, что вам не нужно вдаваться в подробности того, как эта последовательность рисует требуемые дуги, поскольку все значения рассчитаны с помощью библиотеки D3, которая содержит простые функции рисования сложных фигур. Очень скоро я покажу вам JavaScript-код D3, который выполняет все необходимые расчёты и динамически генерирует полный код SVG для рисунка 4.

Пять тегов <path> для каждого круга — это всё, что вам нужно для рисования дуг. Откройте в браузере файл Listing7.svg (см. раздел Загрузка), и вы увидите цветные дуги без хорд, как показано на рисунке 6:

Рисунок 6. Цветные дуги
Colored arcs
Colored arcs

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

Рисование хорд

Подобно дугам, хорды рисуются тегом <g>, который служит упаковкой для пяти тегов <path>, по одному на каждую хорду, как показано в листинге 8:

Листинг 8. Листинг 8. Код SVG для рисования набора из пяти хорд
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
 width="500" height="500">
  <g transform="translate(250,250)">

      <!--Drawing SVG code for arcs goes here. -->  

    <g>
      <path d="M9.18454765366783e-15,-150A150,150 0 0,1 
        19.52349842170555,-148.72401624948697Q 0,0 
        9.18454765366783e-15,-150Z" 
        style="fill: #0000ff;">
      </path>
        <path d="M19.52349842170555,- 148.72401624948697A150,150 
        0 0,1 41.344235247425466,-144.18964668729006Q 0,0 

        140.3723599135688,-52.873439194887816A150,150 0 0,1 
        144.99722485611238,-38.41620470616511Q 0,0 
        19.52349842170555,-148.72401624948697Z" 
        style="fill: #ffd700;">
      </path>
      <path d="M111.39598065576133,-100.4536484839712A150,150        
        0 0,1 140.3723599135688,-52.873439194887816Q 0,0 
        84.8772338498757,123.67641316756206A150,150 0 0,1 
        50.93706344958661,141.08655345968583Q 0,0 
        111.39598065576133,-100.4536484839712Z" 
        style="fill: #008000;">
      </path>
      <path d="M-149.71409635840777,9.256854302914952A150,150 
        0 0,1 -140.64142529945457,-52.15351847898602Q 0,0 
        68.6764412000974,-133.35496400243062A150,150 0 0,1 
        111.39598065576133,-100.4536484839712Q 0,0 -
        149.71409635840777,9.256854302914952Z" 
        style="fill: #ff0000;">
      </path>
      <path d="M-59.58349836433014,-137.65829696268898A150,150 
        0 0,1 -1.6078040591602227e-13,-150Q 0,0 
        41.344235247425466,-144.18964668729006A150,150 0 0,1 
        68.6764412000974,-133.35496400243062Q 0,0 -
        59.58349836433014,-137.65829696268898Z" 
        style="fill: #800000;">
      </path>

    </g>
  </g>
</svg>

Как видите, все теги <path> для хорд в листинге 8 (как и теги в листинге 7) имеют длинное и сложное значение атрибута d, рассчитываемого библиотекой D3.

В листинге 8 теги <path> для дуг отсутствуют. Если открыть код SVG листинга 8 в браузере, вы увидите хорды без дуг (т.е. без периметра круга), как показано на рисунке 7:

Рисунок 7. Пять наборов хорд без дуг
chords without arcs
chords without arcs

Теперь я покажу вам основанный на D3 код JavaScript для генерации кода SVG рисунка 4.

Применение D3 для рисования кругов с хордами

Код JavaScript в листинге 9 содержит пять шагов, которые используют D3 для создания кода SVG для рисунка 4:

Листинг 9. Листинг 9. Пять шагов для генерации кода SVG для рисунка 4
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>

//Шаг 1:
var navigation = [
  [3057, 3483, 8749, 8465, 4598],
  [ 2371, 7397, 4589, 2861, 8249],
  [ 5972, 5672, 9152, 9725, 8983],
  [ 9763,  8462,  9782, 1953, 5182],
  [ 9567,  1571,  2895, 2783, 1874]
];

var navigationChord = d3.layout.chord()
                      .matrix(navigation);

var chordCalculations = navigationChord.chords;

var colors = [	"blue",
			"gold", 
			"green", 
			"red", 
			"maroon"
		];

var width = 500, height = 500, radius = 150, arcStroke = 40;

//Шаг 2:
var svg = d3.select("body")
            .append("svg")
            .attr("width", width)
            .attr("height", height)

//Шаг 3:
            .append("g")
            .attr("transform", function(d,i){
              return "translate(" + width/2 + "," + height / 2 + ")"
            });

//Шаг 4:
svg.selectAll("path")
.data(navigationChord.groups())
.enter()
.append("path")
.style("fill", function(d, i) { return colors[i]; })
.attr("d", d3.svg.arc().innerRadius(radius)
                       .outerRadius(radius+arcStroke));

//Шаг 5:
navigationChord.sortSubgroups(d3.ascending);
svg.append("g")
   .selectAll("path")
   .data(chordCalculations)
   .enter()
   .append("path")
   .attr("d", d3.svg.chord().radius(radius))
   .style("fill", function(d, i) {
     if (i < colors.length) return colors[i]})
   .style("opacity", function(d, i){
     if (!(i < colors.length)) return 0 });

</script>
</body>

Шаг 1 сохраняет данные и наборы переменных. Я сохраняю данные о переходах в массиве с именем navigation и затем передаю их в d3.layout, который внутренне обрабатывает компоновку. Я сохраняю расчеты, выполненные в d3.layout, в переменной с именем chordCalculations, которая используется на шаге 5 для рисования хорд.

Шаг 2 создает упаковку <svg> и устанавливает её размеры.

Шаг 3 добавляет тег <g>, который служит упаковкой для всех тегов рисования.

Шаг 4 создаёт пять тегов <path> для дуг, код SVG которых вы видели в листинге 7. Затем используется d3.svg.arc(), который выполняет все вычисления для создания атрибутов d для всех пяти тегов цветных дуг <path> .

Шаг 5 создаёт теги <path> для хорд. Сначала я располагаю хорды в возрастающем порядке, а потом передаю расчеты хорд в D3 четырьмя волшебными шагами: .selectAll().data().enter().append(). Наконец, d3.svg.chord() создаёт значения атрибутов d тегов <path>, которые рисуют пять хорд.

В следующий раз

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


Ресурсы для скачивания


Похожие темы

  • Оригинал статьи: Data visualization, Part 1: Visualize browsing metrics with SVG and D3.
  • Масштабируемая векторная графика: посетите официальный сайт SVG и познакомьтесь со спецификацией SVG (и в том числе со списком официально поддерживаемых цветов), вики-сайтом SVG и другими ресурсами.
  • SVG в Википедии:: узнайте больше о SVG.
  • Преобразователь цветов Yellowpipe: этот сайт предлагает преобразователь формата цветов. Здесь вы найдёте дополнительную информацию о представлении цвета в формате RGB и шестнадцатеричном формате.
  • Scalable Vector Graphics in HTML5 (Масштабируемая векторная графика в HTML5) (Джереми Висхюзен (Jeremy Wischusen), developerWorks, январь 2012 г.): эта статья является хорошим введением в SVG.
  • D3: посетите официальный сайт D3 и взгляните на изумительную графику в галерее примеров.
  • IBM ManyEyes: познакомьтесь с тысячами примеров визуализации или создайте свои собственные. Этот экспериментальный сайт создан для того, чтобы каждый мог выгрузить данные, визуализировать их и обсудить свои открытия с другими.
  • D3: скачайте библиотеку D3 JavaScript.
  • IBM Business Process Manager: узнайте больше о семействе продуктов IBM Business Process Manager.
  • Быстрее реального времени: об изменениях в поведении потребителей цифровой электроники: эта презентация делится идеями об изучении поведения потребителей путём анализа больших объёмов данных.
  • Integrate social networks into BPM, Part 1: Collect Twitter data(Интеграция социальных сетей в BPM: Часть 1. Сбор данных из Твиттера) (Билал Сиддикви (Bilal Siddiqui), developerWorks, май 2011 г.) и Integrate social networks into BPM, Part 2: Add Twitter data to CRM (Интеграция социальных сетей в BPM: Часть 2. Добавление данных из Твиттера в CRM-систему) (Билал Сиддикви (Bilal Siddiqui), developerWorks, июнь 2011 г.): в этих статьях обсуждается интеграция социальных ресурсов с приложениями BPM.
  • InfoSphere BigInsights: получите пробную версию IBM InfoSphere BigInsights и анализируйте большие объёмы структурированных и неструктурированных данных.
  • InfoSphere Streams: получите пробную версию IBM InfoSphere Streams и создайте приложения, которые оперативно вводят, анализируют и сопоставляют информацию по мере её поступления в реальном времени из нескольких тысяч источников.
static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Web-архитектура
ArticleID=978119
ArticleTitle=Визуализация данных: Часть 1. Визуализация метрик посещения с помощью графики SVG и D3
publish-date=07172014