Расчет временных рядов


Главная » Ресурсы » Здесь

Большинство коммерческих операций в любой сфере деятельности могут быть точно представлены в виде временных рядов : история продаж , история закупок, история цен и т. д. Работа с временными рядами является ключевым приоритетом для наших клиентов, поэтому Envision обеспечивает широкую поддержку данного типа сценариев. В частности, Envision позволяет объединять данные за день, неделю и месяц. Envision также поддерживает более сложные виды анализа временных рядов, например отставание рядов или расчет плавающих средних значений. Эти функции показаны и описаны на данной странице.

Образец сценария

Мы используем образец набора данных для демонстрации возможностей Envision. В данном разделе показаны более сложные функции Envision, поэтому мы рекомендуем для начала ознакомиться со статьями о ведении расчетов и объединении данных.
read "/sample/Lokad_Items.tsv"
read "/sample/Lokad_Orders.tsv" as O
read "/sample/Lokad_PurchaseOrders.tsv" as PO

show label "Time-series calculations" a1f1 tomato

end := max(date)
lastMon := monday(end)

Week.sold := sum(O.NetAmount)
when date >= end - 52 * 7
  show linechart "Weekly sales" a2f4 tomato unit:"$" with 
    Week.sold as "This year"
    Week.sold[-52] as "Last year"

Week.ma := sum(O.NetAmount / 4) over [-3 .. 0]
when date >= end - 52 * 7
  show linechart "Weekly sales with 4 weeks moving average" a5f7 tomato unit:"$" with 
    Week.ma as "This year"
    Week.ma[-52] as "Last year"

Day.cashFlow := sum(O.NetAmount) - sum(PO.NetAmount)
Day.balance := avg(Day.cashFlow) over [-13 .. 0]
when date >= lastMon - 6 * 7 & date < lastMon
  show linechart "Cash flow over the last 6 weeks" a8f10 tomato unit:"$" with 
    Day.balance as "Balance"

PO.Price = PO.NetAmount / PO.Quantity
O.PurchasePrice = latest(PO.Price)
O.Cost = O.PurchasePrice * O.Quantity
O.Profit = O.NetAmount - O.Cost
Week.profitblty := sum(O.Profit) / sum(O.Cost) or 1
when date >= lastMon - 13 * 7 & date < lastMon
  show linechart "Profitability over the last 13 weeks" a11f13 tomato unit:"%" with 
    Week.profitblty
После выполнения данного сценария с использованием образца набора данных будет создана следующая панель управления.

Image

Виртуальные календари

Объединение данных за день, неделю и месяц является универсальным приемом, поэтому Envision поддерживает такие периодические календарные структуры. В частности, Envision использует три виртуальных таблицы с именами Day, Week и Month соответственно. Эти таблицы называются «виртуальными», потому что они не представлены в виде табличных файлов. Другими словами, эти таблицы существуют только во время выполнения сценария. Сценарий выше использует эти три таблицы для отображения линейных графиков. Взглянем еще раз на соответствующие строки кода.
Week.sold := sum(O.NetAmount)
end := max(date)
when date >= end - 52 * 7
  show linechart "Weekly sales" a2f4 tomato unit:"$" with 
    Week.sold as "This year"
    Week.sold[-52] as "Last year"
В строке 1 содержимое таблицы O суммируется и представляется в виде таблицы Week. В данном сценарии мы используем операцию скалярного присваивания :=, поэтому за каждую неделю здесь высчитывается единственное значение. В строке 3 задается фильтр, который исключает данные, возраст которых превышает 52 недели. Наконец, в строках 4–6 задается линейный график, отражающий два временных ряда. Второй временной ряд Week.sold[-52] имеет оператор отставания, который мы рассмотрим в следующем разделе. Этот сценарий можно легко изменить, используя ежедневное или ежемесячное объединение данных. Например, можно добавить следующие строки в самом конце сценария:
Day.sold := sum(O.NetAmount)
show linechart "Daily sales" a14f16 tomato unit:"$" with
  Day.sold
Month.sold := sum(O.NetAmount)
show linechart "Monthly sales" a17f19 tomato unit:"$" with
  Month.sold
Данный сегмент кода содержит еще два линейных графика, при этом временные ряды объединяются в них ежедневно и ежемесячно соответственно. Переменная Day.sold может пониматься как содержимое столбца sold в таблице Day, но ее также можно понять как равномерный временной ряд с периодом в 1 день в отличие от обычных таблиц Envision, которые содержат колонку Date (например таблица O) и которые можно понимать как неравномерные временные ряды.

Отображение отстающих временных рядов

При анализе временных рядов «отставание» — это оператор, который перемещает элементы временных рядов по времени. Одной из основных целей использования функции отставания временных рядов является сравнение двух различных периодов времени. Envision поддерживает оператор отставания, который как раз предназначен для работы с такими ситуациями. Давайте разберем фрагмент кода, приведенный ранее.
Week.sold := sum(O.NetAmount)
when date >= end - 52 * 7
  show linechart "Weekly sales" a2f4 tomato unit:"$" with 
    Week.sold as "This year"
    Week.sold[-52] as "Last year"
В строках 4 и 5 задаются два временных ряда. Первый временной ряд Week.sold представляет собой данные о продажах, объединенные по неделям. Второй временной ряд имеет дополнительный суффикс [-52]. Этот суффикс сам по себе является оператором отставания. Это означает, что данные, возраст которых превышает 52 недели, перемещаются вперед по временному ряду и отображаются в линейном графике. Когда оператор отставания применяется к таблице Week, аргумент отставания представляет собой целое число, выражающее количество недель. Точно так же, для таблиц Day и Month используются дни и месяцы соответственно.

Оператор отставания активно взаимодействует с оператором фильтрования when. На самом деле, без такого активного взаимодействия фильтр when исключил бы все данные, возраст которых превышает 52 недели, и выполнение операции отставания временного ряда на 52 недели привело бы к отображению нулевых значений. Как видно на изображении панели управления, рассмотренной ранее, оператор отставания правильно перемещает значения за предыдущий год вперед, потому что они не все равны нулю. Это достигается за счет взаимодействия между фильтром when и оператором отставания.

Если навести мышь на линейный график в панели управления, будут отображены даты и значения. В частности, если говорить о сценарии, который приведен в начале раздела, даты, входящие во временной ряд с именем «Прошлый год», на самом деле отличаются от данных из временного ряда «Этот год» на один год. Важно заметить, что на самом деле оператор отставания не сохраняет даты исходных временных рядов. Вместо этого Envision просто использует свой основной принцип работы: если оператор отставания указан в строке, задающей линейный график, то тогда, и только тогда, оригинальные значения дат сохраняются.

Давайте изменим сценарий и применим оператор отставания вне линейного графика. Это можно выполнить путем введения переменной с именем Week.lastYear.
Week.sold := sum(O.NetAmount)
Week.lastYear := Week.sold[-52]
when date >= end - 52 * 7
  show linechart "Weekly sales" a2f4 tomato unit:"$" with 
    Week.sold as "This year"
    Week.lastYear as "Last year"//проблемы отображения даты
Если запустить измененный сценарий и навести мышь на определенные места во временных рядах, вы заметите, что даты в обоих временных рядах совпадают. В данном случае код, отвечающий за отображение дат, невозможно интерпретировать двояко. Например, временной рядWeek.lastYear на самом деле можно интерпретировать как ежегодный прогноз, а в этом случае нам нужно было получить одни и те же даты для двух временных рядов. В заключение, если вы хотите сохранить исходные даты в своем линейном графике для сравнения отстающих временных рядов, то оператор отставания должен быть задан в пределах оператора show.

Объединение данных за временной интервал

В одной из предыдущих статей мы рассматривали способы объединения данных с помощью Envision. С помощью временных рядов можно получить еще один вид объединения данных, который может оказаться очень полезным: объединение данных за определенный интервал времени. Сценарий в начале раздела позволяет рассчитать скользящее среднее значение продаж за 4 недели. Соответствующие строки сценария приведены ниже для удобства.
Week.ma := sum(O.NetAmount / 4) over [-3 .. 0]
when date >= end - 52 * 7
  show linechart "Weekly sales with 4 weeks moving average" a5f7 tomato unit:"$" with 
    Week.ma as "This year"
    Week.ma[-52] as "Last year"
В строке 1 объединение выполняется с помощью агрегатора sum(), и это объединение содержит оператор, который начинается с ключевого слова over в конце сегмента. В строке 2 применяется фильтр, отсекающий данные, которым больше 52 недель. Наконец, в строках с 3 по 5 задано два временных ряда, которые отображаются в линейном графике. Оба временных ряда «сглаживаются» по мере расчета средних значений за 4 недели.

Оператор over используется для определения соответствующего временного интервала, и он должен быть записан следующим образом: [a .. b], где a и b представляют собой целые числа, причем a должно быть меньше или равно b. Единицы, используемые в a и b, зависят от выражения слева от операции присваивания. В данном случае у нас есть таблица Week слева от операции присваивания и в результате -3 и 0 выражаются в неделях.

Обратите внимание: между неделей с показателем -3 и неделей с показателем 0 4 недели, а не 3. В результате мы получаем недели со следующими показателями: -3, -2, -1 и 0.
Параметр over можно использовать со всеми агрегаторами. При использовании данного параметра выражение слева от оператора присваивания обычно представляет собой виртуальный календарь с таблицей Day, Week или Month. Это не обязательное требование, и использовать можно любую таблицу, если в ней есть колонка Date. Кроме того, при использовании таблиц календарей структура over [0 .. 0] дает те же результаты, что и объединение по умолчанию:
Week.sold := sum(O.NetAmount)
// тот же результат!
Week.same := sum(O.NetAmount) over [0 .. 0]

Более сложные способы объединения временных рядов

Синтаксис Envision дает возможность выполнять более сложные расчеты временных рядов. Например, можно рассчитывать временные ряды и производить дальнейшие расчеты на основании исходных временных рядов. Третий блок кода из сценария, приведенного в начале статьи, демонстрирует данную возможность. Соответствующие строки сценария показаны ниже.
Day.cashFlow := sum(O.NetAmount) - sum(PO.NetAmount)
Day.balance := avg(Day.cashFlow) over [-13 .. 0]
when date >= monday(end) - 42 & date < monday(end)
  show linechart "Cash flow over the last 6 weeks" a8f10 tomato unit:"$" with 
    Day.balance as "Balance"
В строке 1 временной ряд Day.cashFlow задан как разность общей суммы продаж и общей суммы закупок. В строке 2 рассчитывается временной ряд Day.balance, который представлен как скользящее среднее значение Day.cashFlow за 14 дней. В строке 3 задается фильтр с двумя специфическими условиями: данные должны быть не старее 7 недель, считая с прошлого понедельника, и данные должны быть не «моложе» прошлого понедельника. Функция monday(), используемая здесь, отвечает за то, чтобы рассматриваемый период времени составлял ровно 6 целых недель. Наконец, в линейном графике, заданном в строках 4 и 5, отображается только временной ряд Day.balance.

В строке 2 происходит объединение с использованием параметра over, который мы описали в предыдущем разделе. Здесь рассчитывается среднее значение для временного ряда за 14 дней. И действительно, между показателями -13 и 0 насчитывается 14 значений. Это объединение не похоже на объединения, которые мы рассматривали ранее, потому что таблица Day появилась слева и справа от операции присваивания, тогда как обычно при объединении, когда не используется суффикс over, данные из одной таблицы преобразуются в другую таблицу.

Также можно проводить повторное объединение ежедневных временных рядов в еженедельные временные ряды. Сценарий, показанный ниже, показывает, как можно рассчитать еженедельный поток денежных средств вместо ежедневного.
Day.cashFlow := sum(O.NetAmount) - sum(PO.NetAmount)
Week.balance := sum(Day.cashFlow / 2) over [-1 .. 0]
when date >= monday(end) - 42 & date < monday(end)
  show linechart "Cash flow over the last 6 weeks" a8f10 tomato unit:"$" with 
    Week.balance as "Balance"

Работа с данными по событиям

Данные по событиям представляют собой определенный способ представления исторических данных, который особое внимание уделяет «изменениям» . Например, вместо представления всей истории цен за каждый день в истории данных гораздо удобнее собирать данные об изменениях истории цен: все изменения цен имеют новые параметры цены и даты, и, предположительно, новая цена не меняется до того момента, пока не будет установлена новая цена. Envision поддерживает сценарии, где исторические данные представляются как список изменений.

Образец набора данных не включает в себя таблицу с историей расценок, однако можно получить примерные данные, исходя из данных о закупках, предположив, что цены не изменяются до момента новой закупки. Именно это происходит в последнем блоке сценария, приведенного в начале раздела. Давайте подробно рассмотрим соответствующий сегмент сценария.
PO.Price = PO.NetAmount / PO.Quantity
O.PurchasePrice = latest(PO.Price)
O.Cost = O.PurchasePrice * O.Quantity
O.Profit = O.NetAmount - O.Cost
Week.profitblty := sum(O.Profit) / sum(O.Cost) or 1
when date >= lastMon - 13 * 7 & date < lastMon
  show linechart "Profitability over the last 13 weeks" a11f13 tomato unit:"%" with 
    Week.profitblty
В строке 1 закупочная цена за единицу рассчитывается в каждой строке таблицы PO с помощью обычных векторных операций Envision. В строке 2 используется функция latest, и она работает очень специфическим образом: для каждой строки таблицы O (таблицы, к которой она применяется) функция latest ищет самую новую строку PO.Price из имеющихся, не новее, чем указанная строка таблицы O, и копирует данное значение слева от операции присваивания. В строках 3 и 4 прописаны дальнейшие расчеты с использованием векторов и объединения. Наконец, в строке 5 задан фильтр ограничения операций периодом в 13 целых недель. В блоке этого фильтра прописан линейный график, включающий в себя временной ряд Week.profitability, который был ранее рассчитан в строке 4.

Функция latest поддерживает расчеты, рассматриваемые в настоящем документе. Данная функция предназначена для работы с событиями, когда одно значение считается постоянным до тех пор, пока оно не будет изменено на новое. В частности, можно «уплотнить» закупочные цены, рассчитывая цену за каждый день истории данных с помощью следующего алгоритма:
Day.PurchasePrice = latest(PO.Price)
На практике функция latest может быть использована для работы со многими ситуациями, такими как дефицит товаров, промоакции, жизненный цикл продукции и т. д.