Визуализация данных Московской Биржи с помощью InterSystems DeepSee. Часть I

в 6:49, , рубрики: deepsee, intersystems, intersystems cache, биржа, Блог компании InterSystems, визуализация данных, котировки

Введение

В стеке технологий 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.

Измерение 2

Заметим, что в измерении есть элементы: собственно измерение, иерархия и уровни этой иерархии (Level). Любое измерение куба состоит как минимум из одной иерархии в котором в простейшем случае всего один уровень.

Показатели (Measures)

Показатели или метрики это такие элементы куба, куда относят какие-либо "количественные" данные, которые необходимо посчитать для "качественных" измерений куба (Dimensions).
Например в таблице фактов такими показателями могут быть свойства Volume (количество акций) и Average (Средняя цена). Перетянем свойство Volume на область показателей и создадим показатель "Количество" с функцией SUM, которая будет считать общее количество акций в текущем срезе.
Добавим также в показатели свойство Average и укажем в качестве функции расчета MAX — расчет максимального значения. С целью использования цены для визуализации изменения максимальной цены акции во времени.

Показатели

Списки (Listings)

Списки — это элементы куба, описывающие способ доступа к исходным данным куба, позволяя перейти от агрегированных к исходным данным куба. Как правило при работе с кубом, аналитик просматривает агрегированную информацию в различных срезах. Однако, часто возникает необходимость посмотреть на исходные факты, которые вошли в текущий срез. Для этого и создаются листинги — они перечисляют набор полей таблицы фактов, который нужно отобразить при переходе к просмотру фактов Drillthrough. Создадим простой листинг нажав кнопку "Добавить элемент":
Листинг 1

Теперь зададим поля таблицы фактов, которые надо выводить. Например выведем информацию о тикерах и колебаних их цены за день (Name, Ticker, "Open", CloseLegal, Low, Average, High):

Листинг 2

Компиляция куба

Итак мы добавили в куб два показателя, два измерения и один листинг — этого вполне достаточно и уже можно посмотреть, что получилось.
Скомпилируем класс куба (Кнопка "Компилировать"). Если ошибок компиляции нет, значит куб создан правильно и можно наполнить его данными.
Для этого нужно нажать "Построить куб" — в результате DeepSee загрузит данные из таблицы фактов в хранилище данных куба.
Для работы с данными куба нам пригодится другое веб-приложение — DeepSee Analyzer.

Построение сводной таблицы (Pivot)

DeepSee Analyzer — визуальное средство для непосредственного анализа данных кубов и подготовки источников данных для дальнейшей визуализации. Для перехода к DeepSee Analyzer откроем Портал Управления Системой → DeepSee → Выбор области → Analyzer. Открывается рабочее окно Аналайзера.
Analyzer
В рабочем окне Аналайзера слева мы видим элементы созданного куба: показатели и измерения. Комбинируя их мы строим запросы к кубу на языке MDX — аналоге языка SQL для многомерных OLAP кубов.
Рассмотрим интерфейс Аналайзера. Справа — поле сводной таблицы. В поле сводной таблицы Аналайзера всегда показывается результат выполнения MDX-запроса. Посмотреть текущий MDX-запрос можно если нажать кнопку Source. При первом открытии куба в поле сводной таблицы по умолчанию показывается количество записей в таблице фактов — в нашем случае это количество записей в классе Ticker.Data. Этому соответствует MDX: SELECT FROM [TICKER].

Чтобы создать сводную таблицу перетянем в поле колонок измерение “Год”. Показателем выберем "Объём". В результате получим таблицу количества проданных акций по годам.
Pivot 1
Далее перетянем измерение “Тикер” в поле колонок и получим уже сводную таблицу количества акций по инструментам, с разбиением по годам:
Pivot 2

Сейчас для каждой ячейки полученной таблицы рассчитывается одна величина — суммарное количество акций участвовавших в сделках (в случае если не выбран ни один показатель, считается количество фактов — в данном случае это можно интерпретировать как количество дней торговли инструмента). Это можно изменить. Добавим показатель "Средняя цена". В результате можно видеть уже более интересную картину: сводная таблица отображает среднюю максимум цены по каждому инструменту за год.

Pivot 3

Как мы помним, в определении куба у нас заложена иерархия по датам. Это значит что по измерению Дата возможна операция DrillDown (переход по иерархии измерения от общего к частному). В Аналайзере двойной щелчок по заголовку измерения приводит переходу к следующему по иерархии измерению (DrillDown). В данном случае двойной клик по году приведет к переходу к месяцам этого года, а двойной клик на месяце — к переходу на уровень дней. В итоге можно посмотреть как менялась средняя цена акции для дней или месяцев.

Pivot 4

На предыдущем этапе мы создали листинг — инструмент перехода от агрегированных данных к исходным фактам. Выберем любую строку сводной таблицы и нажмём кнопку Drillthrough для перехода к листингу:
Drillthrough 2

Следующий этап — визуализация. Перед сохранением упростим сводную таблицу и сохраним её под именем TickersByYears.
Pivot 5

Построение дашборда (Dashboard)

Портал Пользователя — это веб-приложение для создания и использования дашбордов (панелей индикаторов). Дашборды содержат виждеты: таблицы, графики и карты на основе сводных таблиц, созданных аналитиками в Аналайзере.
Для перехода к Порталу Пользователя DeepSee откроем Портал Управления Системой → DeepSee → Выбор области → Портал Пользователя.
Portal
Создадим новый дашборд нажав на стрелку справа → добавить → Добавить индикаторную панель
Portal2

Создадим виджет нажав на стрелку справа → Виджеты → "+" → Линейная диаграмма с маркерами. В качестве источника данных выберем TickersByYears:
Widget

Однако читатель возразит — это же средняя температура по больнице. И будет прав. Добавим фильтрацию по инструменту. Для этого нажмём стрелку справа → Виджеты → Виджет 1 → Элементы управления → "+". Форма создания нового фильтра выглядит следующим образом:
Filter

А вот так выглядит наш виджет с фильтром. Пользователь может изменить значение фильтра на любое другое.
Widget 2

После этого сохраним дашборд.

Установка MDX2JSON и DeepSeeWeb

Для визуализации созданного дашборда можно использовать следующие OpenSource решения:

  • MDX2JSON — REST API предоставляет информацию о кубах, пивотах, дашбордах и многих других элементах DeepSee, в частности — результатах исполнения MDX запросов, что позволяет встраивать пользовательский интерфейс аналитического решения на DeepSee в любое современное Web или мобильное приложение.
  • DeepSeeWeb — AngularJS приложение, предоставляющее альтернативную реализацию портала пользователя DeepSee. Может быть легко кастомизирован. Использует MDX2JSON в качестве бэкэнда. Вот пример дашборда визуализированного в DeepSeeWeb:

DeepSeeWeb

Установка MDX2JSON

Для установки MDX2JSON надо:

  1. Загрузить Installer.xml и импортировать его в любую область с помощью Studio, Портала Управления Системой или Do $System.OBJ.Load(file).
  2. Выполнить в терминале (пользователем с ролью %ALL): Do ##class(MDX2JSON.Installer).setup()

Для проверки установки надо открыть в браузере страницу http://server:port/MDX2JSON/Test?Debug. Возможно потребуется ввести логин и пароль (в зависимости от настроек безопасности сервера). Должна открыться страница с информацией о сервере. В случае получения ошибки, можно почитать на Readme и Wiki.

Установка DeepSeeWeb

Для установки DeepSeeWeb надо:

  1. Загрузить установщик и импортировать его в любую область с помощью Studio, Портала Управления Системой или Do $System.OBJ.Load(file).
  2. Выполнить в терминале (пользователем с ролью %ALL): Do ##class(DSW.Installer).setup()

Для проверки установки надо открыть в браузере страницу http://server:port/dsw/index.html. Должна открыться станица авторизации. В области SAMPLES представлено множество уже готовых дашбордов и все они автоматически отображаются в DeepSeeWeb.

Визуализация

Откроем http://server:port/dsw/index.html и авторизируемся, также нужно указать область с кубом. Откроется список дашбордов, в нашем случае есть только один созданный дашборд "Акции". Откроем его:

DSW

Отображается наш созданный виджет. Для него поддерживается Drilldown и фильтр созданный в Портале Пользователя DeepSee:

DSW 2

Выводы

InterSystems DeepSee является мощным инструментом создания OLAP-решений, предоставляя разработчикам средства для создания и внедрения в свои приложения аналитической OLAP-функциональности, которая способна работать на оперативных базах данных приложений без создания отдельной инфраструктуры для решения аналитических задач. В следующий части я расскажу про различные варианты визуализации данных.

Ссылки

Автор: InterSystems

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js