Система управления базами данных Microsoft SQL Server обладает компонентом интеллектуального анализа данных (Data Mining), который предоставляет набор алгоритмов, позволяющих анализировать табличные и многомерные данные. Этот анализ позволяет предсказывать будущее поведение данных, прогнозировать значения столбцов, искать ассоциации, находить ключевые значения одних столбцов, влияющих на значения других столбцов.
Рассмотрим в данной статье анализ роста прибыли от продажи некоторых абстрактных продуктов. Природа этих продуктов и единица измерения прибыли сейчас не имеют значения. Важно то, что такие данные имеют естественную временную ось. С течением времени прибыль по каждому продукту накапливается с разной скоростью, изменение ее объема может происходить по разным законам в зависимости от типа продукта. Если продуктов сотни или тысячи, а история наблюдений охватывает годы, то прогнозирование будущей прибыли по каждому такому продукту может стать непростой задачей. Компонент Data Mining позволяет во многом автоматизировать поиск закономерностей в изменении данных и прогнозирование значений атрибутов. Поскольку мы рассматриваем задачу с временной осью, то будет полезно воспользоваться алгоритмом временных рядов. Но сначала создадим структуру данных, а также сами данные, в которые будут внедрены определенные закономерности.
Создадим и наполним таблицу продуктов. Для простоты рассмотрим поведение одного продукта, так как создание модели прогнозирования не зависит от числа элементов.
Теперь создадим таблицу с историей доходов по продуктам:
Заполним таблицу с историей прибыли от продуктов на каждый день 2015-ого года. При этом считаем, что особенностью продукта Продукт_1 является то, что с начала каждого квартала прибыль ничтожна, в районе тысячи, и с каждым днем в первые три недели растет как парабола:
Затем в последующие 10 недель объем за каждый день колеблется на одном уровне, отклоняясь выше или ниже на величину не превышающую 20 % от некоторого среднего значения:
Последние 3 недели квартала ознаменовываются тем, что наблюдается резкий скачок в каждодневной прибыли, которая нарастает по экспоненте:
Прогнав эти скрипты для четырех кварталов 2015-ого года и создав сводную таблицу и диаграмму в Excel на основе таблицы dbo.DayIncome, можно увидеть следующее представление данных:
Перед нами стоит задача спрогнозировать поведение объема дневных прибылей продуктов на первое полугодие 2016-ого года. Конечно, внедренные закономерности легко обнаружить в приведенном выше рисунке. Однако в реальности графики могут быть более сложными и не очевидными для невооруженного глаза, да и самих продуктов может быть очень много. Но для всех подобных ситуаций работают одни и те же принципы для распознавания поведения данных.
Прежде чем заняться построением модели прогнозирования требуется создать базу данных Analysis Services. Для этого выполним код на языке XML/A:
Теперь в новой базе данных необходимо создать источник данных, чтобы база данных могла подключаться к привычным таблицам SQL Server. Для создания источника данных создадим хранимую процедуру для Analysis Services на основе clr-сборки. Сначала требуется создать проект Visual C# типа библиотека классов. К нему требуется добавить ссылки для подключения пространств имен Microsoft.AnalysisServices и Microsoft.AnalysisServices.AdomdServer. Эти ссылки добавляются через файлы Microsoft.AnalysisServices.dll и msmgdsrv.dll, которые находятся соответственно в каталогах C:\Program Files (x86)\Microsoft SQL Server\120\SDK\Assemblies и C:\Program Files\Microsoft Analysis Services\AS OLEDB\120 (если у вас установлен MS SQL 2014).
Сборка реализуется в виде класса, в котором есть 2 метода для создания и удаления источников данных:
Думаю в этом коде все понятно. Он повторяет все те действия, которые мы делаем в графическом конструкторе Business Intelligence Development Studio. Сборка иллюстрирует возможности объектной модели AdoMd.Net. C ее помощью можно выполнять административные задачи сервера Analysis Services, запускать код на языках MDX и DMX, выполнять операции с многомерными объектами.
Развернем сборку в базе данных. Это можно сделать с помощью обозревателя объектов в Management Studio:
После развертывания сборки в редакторе запросов dmx можно запустить процедуру создания источника данных:
Теперь можно приступить к проектированию объектов моделирования. Создадим структуру добычи данных:
В метаданных можно проверить, что структура создана:
К структуре Data Mining добавим модель, использующую алгоритм временных рядов:
Свойства созданной модели можно просмотреть таким запросом:
Теперь требуется произвести этап обучения модели. Структуру необходимо наполнить данными за 2015-ый год, в этот момент модель применит алгоритм и создаст паттерны:
После окончания обучения остается только запустить прогнозирующий запрос. Для просмотра прогноза на ближайшие 6 месяцев по первому продукту можно выполнить такой запрос:
Для возможности запуска таких запросов на стороне реляционного сервера создадим связанный сервер:
Создадим такое представление чтобы построить на его основе сводную таблицу в Excel:
Создадим сводную таблицу на новом листе Excel:
Результат не впечатляет. Для улучшения качества прогноза требуется передать модели дополнительные данные, которые есть у нас: мы понимаем, что в распределении данных есть квартальная цикличность. Поэтому необходимо воспользоваться параметром periodicity_hint модели:
Теперь результат предсказания выглядит намного лучше:
В то же время вы видим, что рост в конце квартала не настолько быстрый как на самом деле. Если мы знаем, что рост в последние 20 дней квартала следует одним и тем же тенденциям, то можно дополнить этим знанием модель:
В этот раз все точно:
Метод временных предоставляет и другие возможности. Предположим, что с 2016-ого года поступил новый продукт, который продается только первые десять дней 2016-ого, но за это время динамика у него такая же как и у продукта Продукт_1 за аналогичный период. Если есть уверенность что данный продукт продается с той же скоростью, что и Продукт_1, то можно построить прогноз для нового продукта, используя часть модели, построенную для первого продукта:
У модели временных рядов есть много других параметров. В частности, можно выполнять анализ типа "что если". Например. если объем продаж зависит от определенных параметров продукта, и они входят в структуру как входные параметры, то можно задать определенные значения этих параметров на будущие периоды и получить прогноз на будущие периоды в зависимости от будущих значений дополнительных параметров.
Рассмотрим в данной статье анализ роста прибыли от продажи некоторых абстрактных продуктов. Природа этих продуктов и единица измерения прибыли сейчас не имеют значения. Важно то, что такие данные имеют естественную временную ось. С течением времени прибыль по каждому продукту накапливается с разной скоростью, изменение ее объема может происходить по разным законам в зависимости от типа продукта. Если продуктов сотни или тысячи, а история наблюдений охватывает годы, то прогнозирование будущей прибыли по каждому такому продукту может стать непростой задачей. Компонент Data Mining позволяет во многом автоматизировать поиск закономерностей в изменении данных и прогнозирование значений атрибутов. Поскольку мы рассматриваем задачу с временной осью, то будет полезно воспользоваться алгоритмом временных рядов. Но сначала создадим структуру данных, а также сами данные, в которые будут внедрены определенные закономерности.
Создадим и наполним таблицу продуктов. Для простоты рассмотрим поведение одного продукта, так как создание модели прогнозирования не зависит от числа элементов.
if
object_id ( N'dbo.Products', N'U' ) is null
begin
create table dbo.Products
(
iProductId smallint identity ( 1, 1 ) not null,
vcName varchar ( 100 ) not null,
constraint
PK_Products_iProductId primary
key clustered ( iProductId asc ) on [primary],
constraint
AK_Products_vcName unique
nonclustered ( vcName asc ) on [primary]
) on [primary]
end
go
if
not exists
(
select *
from dbo.Products
where vcName = 'Продукт_1'
)
begin
insert into dbo.Products ( vcName )
values ( 'Продукт_1' )
end
goТеперь создадим таблицу с историей доходов по продуктам:
if
object_id ( N'dbo.DayIncome', N'U' ) is null
begin
create table dbo.DayIncome
(
iRowId int identity ( 1, 1 ) not null,
dtDate date not null,
iProductId smallint not null,
fIncome float not null,
constraint
PK_DayIncome_dtDate_iProductId_iRowId primary key clustered ( dtDate asc, iProductId asc, iRowId asc ),
constraint
FK_DayIncome_iProductId foreign key ( iProductId ) references dbo.Products ( iProductId ) on update cascade on delete no action
) on [primary]
end
goЗаполним таблицу с историей прибыли от продуктов на каждый день 2015-ого года. При этом считаем, что особенностью продукта Продукт_1 является то, что с начала каждого квартала прибыль ничтожна, в районе тысячи, и с каждым днем в первые три недели растет как парабола:
set
nocount on
declare
@iQuarter int = 1
declare
@dtStart date = datefromparts ( 2015, @iQuarter * 3 - 2, 1 ), @dtEnd date, @dtCur date, @iProductId smallint = ( select iProductId from dbo.Products where vcName = 'Продукт_1' )
set
@dtEnd = dateadd ( day, 19, @dtStart )
set
@dtCur = @dtStart
while
@dtCur <= @dtEnd
begin
insert into dbo.DayIncome
(
dtDate,
iProductId,
fIncome
)
values
(
@dtCur,
@iProductId,
1000 +
power ( 300 * day ( @dtCur ), 2 ) +
100 *
round ( rand (), 4 ) * case when cast ( ( 10 * round ( rand (), 1 ) ) as int ) % 2 = 0 then 1 else -1 end
)
set @dtCur = dateadd ( day, 1, @dtCur )
end
set
nocount off
go
Затем в последующие 10 недель объем за каждый день колеблется на одном уровне, отклоняясь выше или ниже на величину не превышающую 20 % от некоторого среднего значения:
set
nocount on
declare
@iQuarter int = 1
declare
@dtStart date = dateadd ( day, 20, datefromparts ( 2015, @iQuarter * 3 - 2, 1 ) ), @dtEnd date, @dtCur date, @iProductId smallint = ( select iProductId from dbo.Products where vcName = 'Продукт_1' )
set
@dtEnd = dateadd ( day, 50, @dtStart )
set
@dtCur = @dtStart
while
@dtCur <= @dtEnd
begin
insert into dbo.DayIncome
(
dtDate,
iProductId,
fIncome
)
values
(
@dtCur,
@iProductId,
36000000 +
7200000 * round ( rand (), 4 ) * case when cast ( ( 10 * round ( rand (), 1 ) ) as int ) % 2 = 0 then 1 else -1 end
)
set @dtCur = dateadd ( day, 1, @dtCur )
end
set nocount off
goПоследние 3 недели квартала ознаменовываются тем, что наблюдается резкий скачок в каждодневной прибыли, которая нарастает по экспоненте:
set
nocount on
declare
@iQuarter int = 1
declare
@dtStart date = dateadd ( day, 71, datefromparts ( 2015, @iQuarter * 3 - 2, 1 ) ), @dtEnd date, @dtCur date,
@iProductId
smallint = ( select iProductId from dbo.Products where vcName = 'Продукт_1' )
set
@dtEnd = dateadd ( day, -1, dateadd ( quarter, 1, datefromparts ( 2015, @iQuarter * 3 - 2, 1 ) ) )
set
@dtCur = @dtStart
while
@dtCur <= @dtEnd
begin
insert into dbo.DayIncome
(
dtDate,
iProductId,
fIncome
)
values
(
@dtCur,
@iProductId,
36000000 +
3600000 * round ( rand (), 4 ) * case when cast ( ( 10 * round ( rand (), 1 ) ) as int ) % 2 = 0 then 1 else -1 end +
20000 *
cast ( 1000 + power ( 2 * ( 1 - 0.01 * ( datediff ( day, @dtStart, @dtCur ) + 1 ) ), ( datediff ( day, @dtStart, @dtCur ) + 1 ) ) as float )
)
set @dtCur = dateadd ( day, 1, @dtCur )
end
set nocount off
goПрогнав эти скрипты для четырех кварталов 2015-ого года и создав сводную таблицу и диаграмму в Excel на основе таблицы dbo.DayIncome, можно увидеть следующее представление данных:
Перед нами стоит задача спрогнозировать поведение объема дневных прибылей продуктов на первое полугодие 2016-ого года. Конечно, внедренные закономерности легко обнаружить в приведенном выше рисунке. Однако в реальности графики могут быть более сложными и не очевидными для невооруженного глаза, да и самих продуктов может быть очень много. Но для всех подобных ситуаций работают одни и те же принципы для распознавания поведения данных.
Прежде чем заняться построением модели прогнозирования требуется создать базу данных Analysis Services. Для этого выполним код на языке XML/A:
<Batch xmlns="http://schemas.microsoft.com/analysisservices/2003/engine">
<Alter AllowCreate="true" ObjectExpansion="ExpandFull">
<Object>
<DatabaseID>ProductModels</DatabaseID>
</Object>
<ObjectDefinition>
<Database
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ddl2="http://schemas.microsoft.com/analysisservices/2003/engine/2"
xmlns:ddl2_2="http://schemas.microsoft.com/analysisservices/2003/engine/2/2"
xmlns:ddl100_100="http://schemas.microsoft.com/analysisservices/2008/engine/100/100"
xmlns:ddl200="http://schemas.microsoft.com/analysisservices/2010/engine/200"
xmlns:ddl200_200="http://schemas.microsoft.com/analysisservices/2010/engine/200/200"
xmlns:ddl300="http://schemas.microsoft.com/analysisservices/2011/engine/300"
xmlns:ddl300_300="http://schemas.microsoft.com/analysisservices/2011/engine/300/300"
>
<ID>ProductModels</ID>
<Name>ProductModels</Name>
<DataSourceImpersonationInfo>
<ImpersonationMode>Default</ImpersonationMode>
</DataSourceImpersonationInfo>
</Database>
</ObjectDefinition>
</Alter>
</Batch>Теперь в новой базе данных необходимо создать источник данных, чтобы база данных могла подключаться к привычным таблицам SQL Server. Для создания источника данных создадим хранимую процедуру для Analysis Services на основе clr-сборки. Сначала требуется создать проект Visual C# типа библиотека классов. К нему требуется добавить ссылки для подключения пространств имен Microsoft.AnalysisServices и Microsoft.AnalysisServices.AdomdServer. Эти ссылки добавляются через файлы Microsoft.AnalysisServices.dll и msmgdsrv.dll, которые находятся соответственно в каталогах C:\Program Files (x86)\Microsoft SQL Server\120\SDK\Assemblies и C:\Program Files\Microsoft Analysis Services\AS OLEDB\120 (если у вас установлен MS SQL 2014).
Сборка реализуется в виде класса, в котором есть 2 метода для создания и удаления источников данных:
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
Microsoft.AnalysisServices.AdomdServer;
using
Microsoft.AnalysisServices;
namespace
DatabaseOper
{
public class DatabaseOper
{
[SafeToPrepare(true)]
public void CreateDataSource(string
DataSourceName, string ConnectionString,
string
sImpersonationMode, string Account, string Password)
{
if
(Context.ExecuteForPrepare)
{
return;
}
Microsoft.AnalysisServices.Server svr = new
Microsoft.AnalysisServices.Server();
svr.Connect("*");
Database
db = svr.Databases.GetByName(Context.CurrentDatabaseName);
DataSource
ds = db.DataSources.Add(DataSourceName, DataSourceName);
ds.ConnectionString =
ConnectionString;
bool
FoundMode = false;
foreach
(ImpersonationMode mode in Enum.GetValues(typeof(ImpersonationMode)))
{
if
(mode.ToString() == sImpersonationMode)
{
FoundMode = true;
ds.ImpersonationInfo = new ImpersonationInfo(mode,
Account, Password);
break;
}
}
if
(!FoundMode)
{
throw
(new SystemException("Недопустимый режим олицетворения."));
}
ds.Update();
}
[SafeToPrepare(true)]
public void DropDataSource(string
DataSourceName)
{
if
(Context.ExecuteForPrepare)
{
return;
}
Microsoft.AnalysisServices.Server svr = new
Microsoft.AnalysisServices.Server();
svr.Connect("*");
Database
db = svr.Databases.GetByName(Context.CurrentDatabaseName);
DataSource
ds = db.DataSources.FindByName(DataSourceName);
if
(ds != null)
{
ds.Drop();
}
}
}
}Думаю в этом коде все понятно. Он повторяет все те действия, которые мы делаем в графическом конструкторе Business Intelligence Development Studio. Сборка иллюстрирует возможности объектной модели AdoMd.Net. C ее помощью можно выполнять административные задачи сервера Analysis Services, запускать код на языках MDX и DMX, выполнять операции с многомерными объектами.
Развернем сборку в базе данных. Это можно сделать с помощью обозревателя объектов в Management Studio:
После развертывания сборки в редакторе запросов dmx можно запустить процедуру создания источника данных:
call
DatabaseOper.CreateDataSource
(
'DataStructure',
'Provider=SQLNCLI11.1;Data
Source=1-ПК;Integrated
Security=SSPI;Initial Catalog=Sales',
'ImpersonateServiceAccount',
'',
''
)Теперь можно приступить к проектированию объектов моделирования. Создадим структуру добычи данных:
create
mining structure
ProductAnalysis
(
dtDate date key time,
iProductId long key,
fIncome double continuous
);В метаданных можно проверить, что структура создана:
select
*
from
$system.dmschema_mining_structures
where structure_name = 'ProductAnalysis'
;К структуре Data Mining добавим модель, использующую алгоритм временных рядов:
alter
mining structure
ProductAnalysis
add
mining model
ProductsDynamic
(
dtDate,
iProductId,
fIncome predict
) using microsoft_time_series
(
auto_detect_periodicity = 0.99,
forecast_method = 'arima',
minimum_series_value = 1000,
maximum_series_value = 350000000
)
with drillthrough
;Свойства созданной модели можно просмотреть таким запросом:
select
*
from
$system.dmschema_mining_models
where model_name = 'ProductsDynamic'
;Теперь требуется произвести этап обучения модели. Структуру необходимо наполнить данными за 2015-ый год, в этот момент модель применит алгоритм и создаст паттерны:
insert
into mining structure ProductAnalysis ( dtDate, iProductId,
fIncome )
openquery
(
DataStructure,
'
select dtDate,
iProductId, fIncome
from dbo.DayIncome
'
)
;После окончания обучения остается только запустить прогнозирующий запрос. Для просмотра прогноза на ближайшие 6 месяцев по первому продукту можно выполнить такой запрос:
select
flattened PredictTimeSeries
( fIncome, 180 )
from
ProductsDynamic
where iProductId = 1
;Для возможности запуска таких запросов на стороне реляционного сервера создадим связанный сервер:
use
master
go
if
not exists
(
select *
from sys.servers
where name = 'DataMining'
)
begin
exec master.dbo.sp_addlinkedserver @server = N'DataMining', @srvproduct = N'MSOLAP', @provider = N'MSOLAP', @datasrc = N'1-ПК', @catalog = N'DataMining'
end
exec
master.dbo.sp_addlinkedsrvlogin @rmtsrvname = N'DataMining', @useself = N'True', @locallogin = null, @rmtuser = null, @rmtpassword = null
exec
master.dbo.sp_serveroption @server = N'DataMining', @optname = N'collation compatible',
@optvalue = N'true'
exec
master.dbo.sp_serveroption @server = N'DataMining', @optname = N'data access', @optvalue = N'true'
exec
master.dbo.sp_serveroption @server = N'DataMining', @optname = N'rpc', @optvalue = N'true'
exec master.dbo.sp_serveroption @server = N'DataMining', @optname = N'rpc out', @optvalue = N'true'Создадим такое представление чтобы построить на его основе сводную таблицу в Excel:
if
object_id ( N'dbo.GetProductForeCast', N'V' ) is null
begin
exec sp_executesql N'create view dbo.GetProductForeCast as select i = 1'
end
go
alter
view dbo.GetProductForeCast
as
select dtDate = [Expression.$TIME], fIncome = [Expression.fIncome]
from openquery
(
DataMining,
'
select flattened PredictTimeSeries
( fIncome, 185 )
from ProductsDynamic
where iProductId = 1
'
)
go
Создадим сводную таблицу на новом листе Excel:
Результат не впечатляет. Для улучшения качества прогноза требуется передать модели дополнительные данные, которые есть у нас: мы понимаем, что в распределении данных есть квартальная цикличность. Поэтому необходимо воспользоваться параметром periodicity_hint модели:
drop
mining model
ProductsDynamic;
alter
mining structure
ProductAnalysis
add
mining model
ProductsDynamic
(
dtDate,
iProductId,
fIncome predict
) using microsoft_time_series
(
auto_detect_periodicity = 0.99,
forecast_method = 'arima',
periodicity_hint = "{ 92 }",
minimum_series_value = 1000,
maximum_series_value = 350000000
)
with drillthrough
;
delete
from mining structure ProductAnalysis
;
insert
into mining structure ProductAnalysis ( dtDate, iProductId,
fIncome )
openquery
(
DataStructure,
'
select dtDate,
iProductId, fIncome
from dbo.DayIncome
'
)
;Теперь результат предсказания выглядит намного лучше:
В то же время вы видим, что рост в конце квартала не настолько быстрый как на самом деле. Если мы знаем, что рост в последние 20 дней квартала следует одним и тем же тенденциям, то можно дополнить этим знанием модель:
drop mining model ProductsDynamic;
alter mining structure ProductAnalysis
add mining model ProductsDynamic
(
dtDate,
iProductId,
fIncome predict
) using microsoft_time_series
(
auto_detect_periodicity = 0.99,
forecast_method = 'arima',
periodicity_hint = "{ 20, 92 }",
minimum_series_value = 1000,
maximum_series_value = 350000000
)
with drillthrough
;
delete from mining structure ProductAnalysis
;
insert into mining structure ProductAnalysis ( dtDate, iProductId, fIncome )
openquery
(
DataStructure,
'
select dtDate, iProductId, fIncome
from dbo.DayIncome
'
)
;В этот раз все точно:
Метод временных предоставляет и другие возможности. Предположим, что с 2016-ого года поступил новый продукт, который продается только первые десять дней 2016-ого, но за это время динамика у него такая же как и у продукта Продукт_1 за аналогичный период. Если есть уверенность что данный продукт продается с той же скоростью, что и Продукт_1, то можно построить прогноз для нового продукта, используя часть модели, построенную для первого продукта:
if
object_id ( N'dbo.GetNewProdPred', N'V' ) is null
begin
exec sp_executesql N'create view dbo.GetNewProdPred as select i = 1'
end
go
alter
view dbo.GetNewProdPred
as
select dtDate = [Expression.$TIME], fIncome = [Expression.fIncome]
from openquery
(
DataMining,
'
select flattened PredictTimeSeries
( fIncome, 185 )
from
ProductsDynamic
prediction join
(
select ''2016-01-01''
as dtDate, 200000 as fIncome, 1 as iProductId
union
select ''2016-01-02''
as dtDate, 1000000 as fIncome, 1 as iProductId
union
select ''2016-01-03''
as dtDate, 3000000 as fIncome, 1 as iProductId
union
select ''2016-01-04''
as dtDate, 8000000 as fIncome, 1 as iProductId
union
select ''2016-01-05''
as dtDate, 12000000 as fIncome, 1 as iProductId
union
select ''2016-01-06''
as dtDate, 15000000 as fIncome, 1 as iProductId
union
select ''2016-01-07''
as dtDate, 17000000 as fIncome, 1 as iProductId
union
select ''2016-01-08''
as dtDate, 23000000 as fIncome, 1 as iProductId
union
select ''2016-01-09''
as dtDate, 29000000 as fIncome, 1 as iProductId
union
select ''2016-01-10''
as dtDate, 43000000 as fIncome, 1 as iProductId
) as src on
ProductsDynamic.dtDate
= src.dtDate and
ProductsDynamic.fIncome
= src.fIncome and
ProductsDynamic.iProductId
= src.iProductId
'
)
go
У модели временных рядов есть много других параметров. В частности, можно выполнять анализ типа "что если". Например. если объем продаж зависит от определенных параметров продукта, и они входят в структуру как входные параметры, то можно задать определенные значения этих параметров на будущие периоды и получить прогноз на будущие периоды в зависимости от будущих значений дополнительных параметров.