Введение
В стеке технологий InterSystems есть технология для разработки аналитических решений DeepSee. Это встраиваемая аналитическая технология и набор инструментов для создания систем поддержки принятия эффективных решений, в том числе, и с применением прогнозных моделей. DeepSee работает со структурированными и неструктурированными данными. Она предназначена для создания OLAP-решений для баз данных Caché и любых реляционных СУБД. InterSystems DeepSee предоставляет разработчикам средства для внедрения в свои приложения аналитической OLAP-функциональности, которая способна работать на оперативных базах данных приложений без создания отдельной инфраструктуры для решения аналитических задач.
В статье рассматривается пример создания в OLAP-куба, работа со средствами аналитики и построение пользовательского интерфейса на примере анализа котировок акций торгуемых на Московской Бирже.
Этапы
- Получение данных
- ETL
- Построение куба
- Построение сводной таблицы
- Построение дашборда
- Визуализация
Получение данных
Для визуализации данных о котировках акций необходимо их сначала загрузить. У Московской Биржи есть публичное задокументированное API, которое предоставляет информацию о торговле акциями в форматах HTML, XML, JSON, CSV.
Вот, к примеру, XML данные за 27 мая 2013 года. Создадим XML-Enabled класс Ticker.Data
в платформе InterSystems:
Ticker.Data
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-функциональности, которая способна работать на оперативных базах данных приложений без создания отдельной инфраструктуры для решения аналитических задач. В следующий части я расскажу про различные варианты визуализации данных.