Введение
В стеке технологий InterSystems есть технология для разработки аналитических решений DeepSee. Это встраиваемая аналитическая технология и набор инструментов для создания систем поддержки принятия эффективных решений, в том числе, и с применением прогнозных моделей. DeepSee работает со структурированными и неструктурированными данными. Она предназначена для создания OLAP-решений для баз данных Cache и любых реляционных СУБД. InterSystems DeepSee предоставляет разработчикам средства для внедрения в свои приложения аналитической OLAP-функциональности, которая способна работать на оперативных базах данных приложений без создания отдельной инфраструктуры для решения аналитических задач.
В статье рассматривается пример создания в OLAP-куба, работа со средствами аналитики и построение пользовательского интерфейса на примере анализа котировок акций торгуемых на Московской Бирже.
Этапы
- Получение данных
- ETL
- Построение куба
- Построение сводной таблицы
- Построение дашборда
- Визуализация
Получение данных
Для визуализации данных о котировках акций необходимо их сначала загрузить. У Московской Биржи есть публичное задокументированное API, которое предоставляет информацию о торговле акциями в форматах HTML, XML, JSON, CSV.
Вот, к примеру, XML данные за 27 мая 2013 года. Создадим XML-Enabled класс Ticker.Data
в платформе InterSystems:
Class Ticker.Data Extends (%Persistent, %XML.Adaptor)
{
/// Дата торгов
Property Date As %Date(FORMAT = 3, XMLNAME = "TRADEDATE", XMLPROJECTION = "attribute");
/// Краткое название компании
Property Name As %String(XMLNAME = "SHORTNAME", XMLPROJECTION = "attribute");
/// Тикер
Property Ticker As %String(XMLNAME = "SECID", XMLPROJECTION = "attribute");
/// Количество сделок
Property Trades As %Integer(XMLNAME = "NUMTRADES", XMLPROJECTION = "attribute");
/// Общая сумма сделок
Property Value As %Decimal(XMLNAME = "VALUE", XMLPROJECTION = "attribute");
/// Цена открытия
Property Open As %Decimal(XMLNAME = "OPEN", XMLPROJECTION = "attribute");
/// Цена закрытия
Property Close As %Decimal(XMLNAME = "CLOSE", XMLPROJECTION = "attribute");
/// Цена закрытия официальная
Property CloseLegal As %Decimal(XMLNAME = "LEGALCLOSEPRICE", XMLPROJECTION = "attribute");
/// Минимальная цена акции
Property Low As %Decimal(XMLNAME = "LOW", XMLPROJECTION = "attribute");
/// Максимальная цена акции
Property High As %Decimal(XMLNAME = "HIGH", XMLPROJECTION = "attribute");
/// Средневзвешенная цена акции http://www.moex.com/s1194
/// Может считаться как за день так и не за период.
Property Average As %Decimal(XMLNAME = "WAPRICE", XMLPROJECTION = "attribute");
/// Количество акций участвовавших в сделках
Property Volume As %Integer(XMLNAME = "VOLUME", XMLPROJECTION = "attribute");
}
И напишем загрузчик данных в формате XML. Так как класс у нас XML-Enabled то конвертация из XML в объекты класса Ticker.Data происходит автоматически. Аналогичного поведения можно достичь для данных в форматах JSON (через динамические объекты) и CSV (используя %SQL.Util.Procedures). Так как API отдаёт данные за определённую дату (день) то нам надо итерировать по дням и сохранять поступающие данные. Кроме того данные о котировках акций приходят страницами по 100 записей. Загрузчик может выглядеть так:
/// Загрузить информацию об акциях начиная с From и заканчивая To. Purge - удалить все записи перед началом загрузки
/// Формат From, To - YYYY-MM-DD
/// Write $System.Status.GetErrorText(##class(Ticker.Loader).Populate())
ClassMethod Populate(From As %Date(DISPLAY=3) = "2013-03-25", To As %Date(DISPLAY=3) = {$ZDate($Horolog,3)}, Purge As %Boolean = {$$$YES})
{
#Dim Status As %Status = $$$OK
// Переводим даты во внутренний формат для простоты итерации
Set FromH = $ZDateH(From, 3)
Set ToH = $ZDateH(To, 3)
Do:Purge ..Purge()
For DateH = FromH:1:ToH {
Write $c(13), "Populating ", $ZDate(DateH, 3)
Set Status = ..PopulateDay(DateH)
Quit:$$$ISERR(Status)
}
Quit Status
}
/// Загрузить данные за день. Данные загружаются страницами по 100 записей.
/// Write $System.Status.GetErrorText(##class(Ticker.Loader).PopulateDay($Horolog))
ClassMethod PopulateDay(DateH As %Date) As %Status
{
#Dim Status As %Status = $$$OK
Set Reader = ##class(%XML.Reader).%New()
Set Date = $ZDate(DateH, 3) // Преобразовать дату из внутреннего формата в YYYY-MM-DD
Set Count = 0 // Число загруженных записей
While Count '= $G(CountOld) {
Set CountOld = Count
Set Status = Reader.OpenURL(..GetURL(Date, Count)) // Получаем следующую страницу данных
Quit:$$$ISERR(Status)
// Устанавливаем соответствие нода row == объект класса Ticker.Data
Do Reader.Correlate("row", "Ticker.Data")
// Десериализуем каждую ноду row в объект класса Ticker.Data
While Reader.Next(.Object, .Status) {
#Dim Object As Ticker.Data
// Сохраняем объект
If Object.Ticker '="" {
Set Status = Object.%Save()
Quit:$$$ISERR(Status)
Set Count = Count + 1
}
}
Quit:(Count-CountOld)<100 // На текущей странице меньше 100 записей => эта страница - последняя
}
Quit Status
}
/// Получить URL с информацией о котировках акций за дату Date, пропустить первые Start записей
ClassMethod GetURL(Date, Start As %Integer = 0) [ CodeMode = expression ]
{
$$$FormatText("http://iss.moex.com/iss/history/engines/stock/markets/shares/boards/tqbr/securities.xml?date=%1&start=%2", Date, Start)
}
Теперь загрузим данные командой: Write $System.Status.GetErrorText(##class(Ticker.Loader).Populate())
Весь код доступен в репозитории.
ETL
Как известно, для построения OLAP-куба в первую очередь необходимо сформировать таблицу фактов: таблицу операций, записи которой требуется группировать и фильтровать. Таблица фактов может быть связана с другими таблицами по схеме звезда или снежинка.
Таблица фактов для куба обычно является результатом работы аналитиков и разработчиков по процессу, который называется ETL (extract, transform, load). Т.е. из данных предметной области делается “выжимка” необходимых для анализа данных, и переносится в удобную для хранилища структуру "звезда"/"снежинка": факты и справочники фактов.
В нашем случае этап ETL пропустим т.к. наш класс Ticker.Data
уже находятся во вполне удобном для создания куба состоянии.
Построение куба
DeepSee Architect — это веб-приложение для создания OLAP-куба. Для перехода к DeepSee Architect откроем Портал Управления Системой > DeepSee > Выбор области > Architect. Открывается рабочее окно Архитектора.
Возможно нужно будет выбрать область, которая поддерживает DeepSee. В том случае если вы не видите вашей области в списке областей DeepSee перейдите в Портал Управления Системой > Меню > Управление веб-приложениями > /csp/область, и там в поле Включен поставьте галочку DeepSee и нажмите кнопку сохранить. После этого выбранная область должна появиться в списке областей DeepSee.
Создаем новый куб.
Нажав на кнопку "Создать" попадаем на экран создания нового куба, там необходимо установить следующие параметры:
- Имя куба — название куба используемое в запросах к нему
- Отображаемое Имя — локализуемое название куба (перевод осуществляется стандартными механизмами InterSystems)
- Источник Cube — использовать таблицу фактов или другой куб в качестве источника данных
- Исходный класс — если на предыдущем шаге был выбран класс, то указываем в качестве таблицы фактов класс Ticker.Data.
- Имя класса для куба — имя класса, в котором будет храниться определение куба. Создаётся автоматически
- Описание класса — произвольное описание
Вот как выглядит наш новый куб:
Определяем свойства куба
После нажатия кнопки OK будет создан новый куб:
Слева выводятся свойства базового и связанных с ним по “снежинке” классов, которые можно использовать при построении куба.
Центральная часть экрана — это скелет куба. Его можно наполнить свойствами класса с помощью drag-n-drop из области базового класса, либо добавляя элементы вручную. Основными элементами куба являются измерения, показатели и списки.
Измерения (Dimensions)
Измерения — это элементы куба, которые группируют записи таблицы фактов. В измерения обычно относят “качественные” атрибуты базового класса, которые разбивают все записи таблицы фактов по тем или иным срезам. Например нам бы хотелось группировать все факты по названиям инструментов и по датам.
Для разбиения фактов по тикерам прекрасно подойдет свойство Ticker. Перетянем Ticker на область измерений — в результате Архитектор добавит в куб измерение Ticker с одной иерархией H1 и одним уровнем Ticker. Укажем отображаемые названия в подписях к измерению и уровню.
Измерения помимо группировки позволяют строить иерархии вложенности фактов от общего к частному. Типичным примером является измерение по дате, которое обычно часто требуется представить в виде иерархии Год-Месяц-День.
Для свойств типа дата (например как у свойства Date тип %Date) в DeepSee есть специальный тип измерения time, в котором уже предусмотрены часто используемые функции для создания иерархий по дате. Воспользуемся этим и построим трехуровневую иерархию Год-месяц-день с помощью свойства Date.
Заметим, что в измерении есть элементы: собственно измерение, иерархия и уровни этой иерархии (Level). Любое измерение куба состоит как минимум из одной иерархии в котором в простейшем случае всего один уровень.
Показатели (Measures)
Показатели или метрики это такие элементы куба, куда относят какие-либо "количественные" данные, которые необходимо посчитать для "качественных" измерений куба (Dimensions).
Например в таблице фактов такими показателями могут быть свойства Volume (количество акций) и Average (Средняя цена). Перетянем свойство Volume на область показателей и создадим показатель "Количество" с функцией SUM, которая будет считать общее количество акций в текущем срезе.
Добавим также в показатели свойство Average и укажем в качестве функции расчета MAX — расчет максимального значения. С целью использования цены для визуализации изменения максимальной цены акции во времени.
Списки (Listings)
Списки — это элементы куба, описывающие способ доступа к исходным данным куба, позволяя перейти от агрегированных к исходным данным куба. Как правило при работе с кубом, аналитик просматривает агрегированную информацию в различных срезах. Однако, часто возникает необходимость посмотреть на исходные факты, которые вошли в текущий срез. Для этого и создаются листинги — они перечисляют набор полей таблицы фактов, который нужно отобразить при переходе к просмотру фактов Drillthrough. Создадим простой листинг нажав кнопку "Добавить элемент":
Теперь зададим поля таблицы фактов, которые надо выводить. Например выведем информацию о тикерах и колебаних их цены за день (Name, Ticker, "Open", CloseLegal, Low, Average, High):
Компиляция куба
Итак мы добавили в куб два показателя, два измерения и один листинг — этого вполне достаточно и уже можно посмотреть, что получилось.
Скомпилируем класс куба (Кнопка "Компилировать"). Если ошибок компиляции нет, значит куб создан правильно и можно наполнить его данными.
Для этого нужно нажать "Построить куб" — в результате DeepSee загрузит данные из таблицы фактов в хранилище данных куба.
Для работы с данными куба нам пригодится другое веб-приложение — DeepSee Analyzer.
Построение сводной таблицы (Pivot)
DeepSee Analyzer — визуальное средство для непосредственного анализа данных кубов и подготовки источников данных для дальнейшей визуализации. Для перехода к DeepSee Analyzer откроем Портал Управления Системой > DeepSee > Выбор области > Analyzer. Открывается рабочее окно Аналайзера.
В рабочем окне Аналайзера слева мы видим элементы созданного куба: показатели и измерения. Комбинируя их мы строим запросы к кубу на языке MDX — аналоге языка SQL для многомерных OLAP кубов.
Рассмотрим интерфейс Аналайзера. Справа — поле сводной таблицы. В поле сводной таблицы Аналайзера всегда показывается результат выполнения MDX-запроса. Посмотреть текущий MDX-запрос можно если нажать кнопку . При первом открытии куба в поле сводной таблицы по умолчанию показывается количество записей в таблице фактов — в нашем случае это количество записей в классе Ticker.Data. Этому соответствует MDX: SELECT FROM [TICKER]
.
Чтобы создать сводную таблицу перетянем в поле колонок измерение “Год”. Показателем выберем "Объём". В результате получим таблицу количества проданных акций по годам.
Далее перетянем измерение “Тикер” в поле колонок и получим уже сводную таблицу количества акций по инструментам, с разбиением по годам:
Сейчас для каждой ячейки полученной таблицы рассчитывается одна величина — суммарное количество акций участвовавших в сделках (в случае если не выбран ни один показатель, считается количество фактов — в данном случае это можно интерпретировать как количество дней торговли инструмента). Это можно изменить. Добавим показатель "Средняя цена". В результате можно видеть уже более интересную картину: сводная таблица отображает среднюю максимум цены по каждому инструменту за год.
Как мы помним, в определении куба у нас заложена иерархия по датам. Это значит что по измерению Дата возможна операция DrillDown (переход по иерархии измерения от общего к частному). В Аналайзере двойной щелчок по заголовку измерения приводит переходу к следующему по иерархии измерению (DrillDown). В данном случае двойной клик по году приведет к переходу к месяцам этого года, а двойной клик на месяце — к переходу на уровень дней. В итоге можно посмотреть как менялась средняя цена акции для дней или месяцев.
На предыдущем этапе мы создали листинг — инструмент перехода от агрегированных данных к исходным фактам. Выберем любую строку сводной таблицы и нажмём кнопку для перехода к листингу:
Следующий этап — визуализация. Перед сохранением упростим сводную таблицу и сохраним её под именем TickersByYears.
Построение дашборда (Dashboard)
Портал Пользователя — это веб-приложение для создания и использования дашбордов (панелей индикаторов). Дашборды содержат виждеты: таблицы, графики и карты на основе сводных таблиц, созданных аналитиками в Аналайзере.
Для перехода к Порталу Пользователя DeepSee откроем Портал Управления Системой > DeepSee > Выбор области > Портал Пользователя.
Создадим новый дашборд нажав на стрелку справа > добавить > Добавить индикаторную панель
Создадим виджет нажав на стрелку справа > Виджеты > "+" > Линейная диаграмма с маркерами. В качестве источника данных выберем TickersByYears:
Однако читатель возразит — это же средняя температура по больнице. И будет прав. Добавим фильтрацию по инструменту. Для этого нажмём стрелку справа > Виджеты > Виджет 1 > Элементы управления > "+". Форма создания нового фильтра выглядит следующим образом:
А вот так выглядит наш виджет с фильтром. Пользователь может изменить значение фильтра на любое другое.
После этого сохраним дашборд.
Установка MDX2JSON и DeepSeeWeb
Для визуализации созданного дашборда можно использовать следующие OpenSource решения:
- MDX2JSON — REST API предоставляет информацию о кубах, пивотах, дашбордах и многих других элементах DeepSee, в частности — результатах исполнения MDX запросов, что позволяет встраивать пользовательский интерфейс аналитического решения на DeepSee в любое современное Web или мобильное приложение.
- DeepSeeWeb — AngularJS приложение, предоставляющее альтернативную реализацию портала пользователя DeepSee. Может быть легко кастомизирован. Использует MDX2JSON в качестве бэкэнда. Вот пример дашборда визуализированного в DeepSeeWeb:
Установка MDX2JSON
Для установки MDX2JSON надо:
- Загрузить Installer.xml и импортировать его в любую область с помощью Studio, Портала Управления Системой или
Do $System.OBJ.Load(file)
. - Выполнить в терминале (пользователем с ролью %ALL):
Do ##class(MDX2JSON.Installer).setup()
Для проверки установки надо открыть в браузере страницу http://server:port/MDX2JSON/Test?Debug
. Возможно потребуется ввести логин и пароль (в зависимости от настроек безопасности сервера). Должна открыться страница с информацией о сервере. В случае получения ошибки, можно почитать на Readme и Wiki.
Установка DeepSeeWeb
Для установки DeepSeeWeb надо:
- Загрузить установщик и импортировать его в любую область с помощью Studio, Портала Управления Системой или
Do $System.OBJ.Load(file)
. - Выполнить в терминале (пользователем с ролью %ALL):
Do ##class(DSW.Installer).setup()
Для проверки установки надо открыть в браузере страницу http://server:port/dsw/index.html
. Должна открыться станица авторизации. В области SAMPLES представлено множество уже готовых дашбордов и все они автоматически отображаются в DeepSeeWeb.
Визуализация
Откроем http://server:port/dsw/index.html
и авторизируемся, также нужно указать область с кубом. Откроется список дашбордов, в нашем случае есть только один созданный дашборд "Акции". Откроем его:
Отображается наш созданный виджет. Для него поддерживается Drilldown и фильтр созданный в Портале Пользователя DeepSee:
Выводы
InterSystems DeepSee является мощным инструментом создания OLAP-решений, предоставляя разработчикам средства для создания и внедрения в свои приложения аналитической OLAP-функциональности, которая способна работать на оперативных базах данных приложений без создания отдельной инфраструктуры для решения аналитических задач. В следующий части я расскажу про различные варианты визуализации данных.