Подавляющее большинство визуализации данных состоит из статического набора данных, который извлекается по запросу пользователя. Таким образом, данные обновляются только тогда, когда пользователь хочет их обновить. Это шаблон запрос-ответ: пользователь запрашивает информацию, сервер отвечает данными, заполняются визуализации на стороне клиента.
Визуализация данных в реальном времени применима, когда у вас есть данные, которые быстро обновляются в режиме реального времени, и вашему приложению необходимо держать «пульс» и пассивно отслеживать данные. Это означает, что у нас есть графики, которые обновляются автоматически, пока вы не закрываете браузер. Вот несколько примеров, в которых вы можете захотеть получить данные в реальном времени, но не ограничиваясь ими:
- Анализ акций / финансовых инструментов
- Встроенные системы
- Веб-трафик или данные сервера
- Системы безопасности
- геокосмический / 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