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

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

  • Анализ акций / финансовых инструментов
  • Встроенные системы
  • Веб-трафик или данные сервера
  • Системы безопасности
  • геокосмический / gps-мониторинг (think uber
  • бизнес-аналитика
  • маркетинговая разведка
  • промышленные или производственные
  • IoT устройства
  • Все остальное, что требует мониторинга в реальном времени

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

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

Что мы используем

Языки:

  • Python - данные на стороне сервера
  • Javascript - рендеринг диаграмм на стороне клиента

Библиотеки JS:

  • d3.js
  • crossfilter.js
  • dc.js: обеспечивает бесперебойную работу d3 и кросс-фильтра
  • библиотека tornado в Python для веб-сокетов, ioloop и Интернета

Мы собираемся использовать эти инструменты для создания сервера websocket, который каждую секунду публикует фиктивные данные. Затем мы построим несколько статических интерактивных диаграмм с помощью d3, crossfilter и dc.js.

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

Прежде чем мы начнем и настроим

В этом пошаговом руководстве я использую python 2.7.

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

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

Следующий:

  • Установить пип
  • Получите Sublime Text для редактирования текста. Или используйте текстовый редактор по вашему выбору.
  • Создайте новую папку под названием «rt-data-viz».

Создание простого сервера Websocket на Python

Сначала мы создадим наш источник данных. В этом руководстве мы создаем простой сервер веб-сокетов, который периодически отправляет новые данные.

В папке «rt-data-viz» создайте новый файл и сохраните его с именем «websocket_server.py».

Нам нужно установить пакет tornado для запуска наших веб-сокетов и ioloop:

В вашем терминале / консоли запустите pip install tornado

В websocket_server.py мы можем начать кодирование прямо сейчас.

Сначала импортируйте все необходимые пакеты:

import time
import random
import json
import datetime
from tornado import websocket, web, ioloop
from datetime import timedelta
from random import randint

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

Теперь заполним наш обработчик веб-сокетов. Самая важная функция для нас - send_data(). Здесь мы создаем json-объект случайных данных и отправляем его через self.write_message().

После отправки сообщения мы используем ioLoop для создания тайм-аута, который будет периодически отправлять данные. Наконец, мы создаем экземпляр веб-приложения websocket, настраиваем его на прослушивание порта 8001 и запускаем наш экземпляр ioloop. Завершенный websocket_server.py:

Мы можем запустить этот сервер, перейдя в нашу командную строку в папке rt-data-viz и набрав python websocket_server.py. Вы заметите, что ничего не произойдет.

Чтобы сокет стал активным, нам нужно, чтобы наш клиентский код открывал соединение на этом порту. Мы сможем увидеть это после того, как создадим и запустим наш клиентский код.

Создание наших диаграмм с помощью D3 и Crossfilter

Мы собираемся использовать d3.js и crossfilter.js для создания двух диаграмм с одинаковыми данными.

crossfilter помогает нам исследовать многомерные наборы данных с помощью функций, которые могут создавать измерения на основе данных и группировать варианты. dc.js library объединяет вместе d3.js и crossfilter.js, чтобы мы могли использовать сами диаграммы для фильтрации данных при взаимодействии с пользователем .

Посетите их веб-сайты для получения дополнительной информации: d3.js, crossfilter.js, dc.js.

Используя один набор входящих данных, мы собираемся создать две диаграммы:

  • 1 круговая диаграмма, отображающая деньги, потраченные по годам
  • 1 гистограмма, показывающая деньги, потраченные человеком. Это будет выглядеть так:

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

Здесь я выбрал 2014 год на диаграмме, и вы можете увидеть, что данные отражают только 2014 год, когда у Азиза и Джаррода пока нет данных:

Чтобы создать это, добавьте anindex.html file в вашу папку rt-data-viz. Вот базовый шаблон с необходимыми библиотеками:

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

<div id=”chart-ring-year”></div>

<div id=”chart-row-spenders”></div>

Затем мы собираемся начать работу с d3 / crossfilter. Создайте статический массив объектов json с фиктивными данными, например:

var data1 = [ 
 {Name: ‘Ben’, Spent: 330, Year: 2014, ‘total':1},
  {Name: ‘Aziz’, Spent: 1350, Year: 2012, ‘total':2}, 
  {Name: ‘Vijay’, Spent: 440, Year: 2014, ‘total':2}, 
  {Name: ‘Jarrod’, Spent: 555, Year: 2015, ‘total':1},];

Затем мы создаем переменную для хранения наших, в конечном итоге, «перекрестно отфильтрованных» данных: var xfilter = crossfilter(data1);

Имея эти данные, теперь мы можем создавать измерения с помощью функции кроссфильтра dimension(). У нас есть 3 параметра, которые мы будем использовать в этом примере: имя, год и потраченные средства:

var yearDim = xfilter.dimension(function(d) {return +d.Year;});
var spendDim = xfilter.dimension(function(d) {return Math.floor(d.Spent/10);});
var nameDim = xfilter.dimension(function(d) {return d.Name;});

Установив эти размеры, мы можем сгруппировать их. Чтобы немного упростить его, группы - это в основном конечный результат, который заполняет каждую диаграмму. На изображениях выше круговая диаграмма показывает каждый год, а размер каждого кусочка представляет собой сумму, потраченную в этом году. Итак, мы бы назвали нашу группу потратитьсяPerYear и использовали это в нашей диаграмме. Это выглядит так:

var spendPerYear = yearDim.group().reduceSum(function(d) {return +d.Spent;});

И наша вторая диаграмма показывает сумму, потраченную на человека (или «Имя» в нашем json-объекте):

var spendPerName = nameDim.group().reduceSum(function(d) {return +d.Spent;});

Наш код пока выглядит так:

Теперь у нас есть наши данные, измерения и группы. Далее мы визуализируем диаграммы.

Визуализация диаграмм

Давайте создадим функцию рендеринга. Назовем это render_plots(). Он будет отображать диаграммы с помощью функции renderAll() dc.js:

function render_plots(){ #chart plots go here soon #render all the charts dc.renderAll() }

Внутри render_plots() Мы собираемся создать круговую диаграмму, которая будет отображаться в нашем yearRingChart div, который мы создали ранее. На круговой диаграмме мы используем измерение года и группировку trustPerYear. Мы также устанавливаем атрибуты widgth, height и innerRadius, которые характерны для круговых диаграмм:

yearRingChart.width(200).height(200).dimension(yearDim).group(spendPerYear).innerRadius(50);

Для rowChart / гистограммы настраиваем аналогично:

spenderRowChart.width(250).height(200).dimension(nameDim).group(spendPerName);

Сделайте диаграммы «эластичными»

Мы собираемся добавить намного больше данных в эту столбчатую диаграмму, поэтому нам нужно будет динамически изменять ее размер, чтобы она «соответствовала» большим наборам данных. Мы делаем это, устанавливая для elasticX значение true:

spenderRowChart.width(250).height(200).dimension(nameDim).group(spendPerName).elasticX(true);

И теперь мы можем видеть код наших функционирующих диаграмм ... вот наш последний d3.js + crossfilter.js + dc.js, который вызывает функцию render_plot ():

Это круто, вы захотите увидеть это в действии. Вам нужно будет запустить локальный веб-сервер и указать его на свой файл: Откройте папку rt-data-viz в командной строке / терминале и введите:

python -m SimpleHTTPServer 3000

Примечание от timeless less:

Модуль SimpleHTTPServer был объединен с http.server в Python 3.0. Инструмент 2to3 автоматически адаптирует импорт при преобразовании ваших источников в 3.0.

поэтому для python 3 команда: py -m http.server 3000

Не закрывайте терминал и откройте вкладку в выбранном веб-браузере. Перейдите по адресу http: // localhost: 3000 /, чтобы увидеть его вживую.

Обновление графиков D3 с помощью обновлений в реальном времени с сервера Websocket

Итак, теперь у нас есть сервер websocket, который отправляет новые данные каждую секунду или около того, и у нас есть несколько статических диаграмм, которые демонстрируют функциональность d3 и кросс-фильтра. Нам нужно заставить их говорить сейчас.

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

В javascript нашего индексного файла нам нужно создать новое соединение с websocket, которое подключается к нашему работающему websocket_server.py через порт 8001. Сделайте это, создав новый websocket следующим образом:

var connection = new WebSocket(‘ws://localhost:8001/websocket’);

Затем нам нужна функция, которая будет обновлять наши диаграммы каждый раз, когда веб-сокет публикует обновление. Мы делаем это с помощью функции onmessage() Websocket:

connection.onmessage = function(event){

//get data & parse
var newData = JSON.parse(event.data);

#### put data into an array of json objects
var updateObject = [{“Name”: newData.Name,“Year” : newData.Year,“Spent”: newData.Spent,“payType: newData.payType }]

####add this new array into our data
xfilter.add(updateObject);

####redraw our charts with new data
dc.redrawAll();

Вот функция в коде:

А вот весь index.html, который будет работать с вашим сервером websocket для получения данных по мере их публикации:

Так как же наконец собрать все это воедино? Нам действительно нужно развернуть два сервера для запуска кода websocket_server и клиентской стороны соответственно.

Если вы еще этого не поняли, вы сначала запускаете сервер, а затем - клиента. Откройте командную строку в папке rt-data-viz и запустите веб-сокет:

python websocket_server.py

Затем откройте другое приглашение и запустите клиент:

python -m SimpleHTTPServer 3000

Примечание от timeless less:

Модуль SimpleHTTPServer был объединен с http.server в Python 3.0. Инструмент 2to3 автоматически адаптирует импорт при преобразовании ваших источников в 3.0.

поэтому для python 3 команда: py -m http.server 3000

Откройте свой localhost: 3000 в браузере, и через несколько секунд вы увидите, как обновляются графики! Они не только обновляются новыми данными с сервера websocket, но вы также можете щелкнуть диаграммы, и они будут перекрестно фильтровать друг друга.

Заключение

Мы рассмотрели веб-сокеты, d3, кросс-фильтр и dcjs. Надеюсь, вы извлекли большую пользу из моих усилий здесь! Дайте мне знать, что вы думаете. Напишите мне в Твиттере @benjaminmbrown, чтобы получить самые быстрые ответы.

Весь код доступен в моем репозитории с учебником на github:

Https://github.com/benjaminmbrown/real-time-data-viz-d3-crossfilter-websocket-tutorial

Контакт

Linkedin: Бенджамин Майклбраун

Twitter: benjaminmbrown

Github: benjaminmbrown