Оконные функции в Yandex DataLens
Оконные функции являются аналогом агрегатных функций. Они позволяют получить дополнительную информацию об исходной выборке, например, вычислить нарастающий итог, скользящее среднее или ранжировать значения.
Отличие оконных функций от агрегатных состоит в том, что при вычислении оконных функций строки не объединяются в одну, а продолжают существовать отдельно. Результат вычисления функции при этом отображается в каждой строке. Таким образом, исходное количество строк в результате вычисления оконной функции не изменяется. Подробнее о том, как работают агрегация и группировка данных в DataLens, читайте в разделе Агрегирование данных в DataLens.
В качестве исходных данных для примеров будет использоваться файл Selling.csv
Применение оконных функций
В DataLens аргументами оконных функций могут быть только показатели. Группы значений, для которых вычисляется функция, задаются в виде списка измерений и называются окнами. Для группировки могут применяться только измерения, участвующие в построении чарта. К ним относятся все измерения, которые находятся в одной из секций чарта.
В качестве исходных данных рассмотрим таблицу Selling, содержащую данные о продажах в городах:
| # | City | Category | Date | Sales | Profit | Day's discount |
|---|---|---|---|---|---|---|
| 1 | Detroit | Office Supplies | 2014-01-02 | 10 | 7 | 0,05 |
| 2 | Portland | Office Supplies | 2014-04-05 | 14 | 10 | 0,00 |
| 3 | Portland | Office Supplies | 2014-01-21 | 20 | 12 | 0,20 |
| 4 | San Francisco | Office Supplies | 2014-03-11 | 8 | 3 | 0,10 |
| 5 | Detroit | Furniture | 2014-01-01 | 12 | 3 | 0,00 |
| 6 | Portland | Furniture | 2014-01-21 | 7 | 2 | 0,05 |
| 7 | San Francisco | Technology | 2014-01-02 | 7 | 3 | 0,10 |
| 8 | San Francisco | Technology | 2014-01-17 | 13 | 5 | 0,20 |
Пример 1
В чарте на основе таблицы Selling с группировкой по измерениям City и Category необходимо посчитать общую сумму продаж (TotalSales), а также долю каждой категории в городе от общей суммы (% Total). Для этого нужно создать два показателя с помощью оконной функции SUM:
- TotalSales —
SUM(SUM([Sales]) TOTAL) - % Total —
SUM([Sales]) / [TotalSales]
На примере чарта Таблица результат будет выглядеть так:

Пример 2
Необходимо упорядочить строки в таблице Selling в зависимости от величины суммы продаж. Для этого можно использовать оконную функцию RANK — RANK(SUM([Sales])). В результате каждой строке будет присвоен свой порядковый номер: строке с наибольшей суммой продаж — номер 1, строке с наименьшей суммой — номер 6.

Оконные функции могут быть вложены друг в друга. При этом можно указать свою группировку для каждой функции, использующейся в формуле.
Пример 3
Необходимо упорядочить строки в таблице Selling в зависимости от величины средней суммы продаж за все даты в городе. Среднюю сумму продаж по городу можно вычислить с помощью функции AVG — AVG(SUM([Sales]) WITHIN [City]). Названия городов в таблице повторяются, поэтому для ранжирования лучше использовать функцию RANK_DENSE — она не пропускает порядковые номера для строк с одинаковым значением. В результате получается следующая формула — RANK_DENSE(AVG(SUM([Sales]) WITHIN [City]) TOTAL).

Пример 4
Рассмотрим более сложный пример использования оконных функций. В качестве источника построим датасет на основе подключения к демонстрационной БД (таблица SampleLite). Построим график статистики продаж по подкатегориям товаров. На графике отобразим только те подкатегории, которые хотя бы один раз входили в топ-3 по продажам за день.
Подробнее
-
Упорядочим подкатегории товаров в пределах каждой даты по убыванию суммы продаж. Для этого создадим показатель с помощью оконной функции RANK:
- Sales Rank —
RANK(SUM([Sales]) WITHIN [Date])
В результате в пределах каждой даты подкатегории с максимальной суммой продаж будет присвоено значение
1, следующей по сумме категории — значение2и т.д. Для удобства разместим данные в чарте типа Таблица:
- Sales Rank —
-
Отметим подкатегории, которые попадают в топ-3 по продажам в рамках одной даты. Для этого создадим показатель:
- Top-3 —
IF([Sales Rank] <= 3, 1, 0)
Для подкатегорий товаров, которые попадают в топ-3 по продажам в рамках даты, показатель
[Top-3]будет равен1, а для всех остальных категорий в рамках той же даты —0.
- Top-3 —
-
С помощью показателя
[Top-3]мы отметили категории в рамках одной даты. Теперь нужно отметить эти подкатегории в остальных датах. Для этого создадим показатель с помощью оконной функции MAX:- [Show Category] —
MAX([Top-3] WITHIN [Sub-Category])
Для каждой подкатегории товаров показатель
[Show Category]будет равен1не только в рамках той даты, когда она была в топ-3 по продажам, но и для всех остальных дат. Если подкатегория ни в один из дней не входила в топ-3 по продажам, показатель[Show Category]для нее будет равен0.
- [Show Category] —
-
Добавим фильтр в чарт:
[Show Category] = 1. Таким образом мы получили список подкатегорий товаров, которые нужно отобразить на графике. -
Теперь поменяем тип чарта на Линейная диаграмма. Настроим визуализацию:
-
в секцию X перетащим измерение
Date; -
в секцию Y перетащим показатель
Sales; -
в секцию Цвета перетащим измерение
Sub-Category; -
в секции Фильтры оставим фильтр по значению
1показателяShow Category; -
в настройках оси Y для опции Пустые значения (null) установим значение Отображать как 0.

-
Группировка в оконных функциях
Так же, как и агрегатные функции, оконные функции могут быть вычислены:
- для одного окна;
- для нескольких окон.
Подробнее о группировке в оконных функциях читайте в разделе Группировка.
Группировка для одного окна
При таком варианте группировки функция вычисляется для единственного окна, в которое попадают все строки. Для этого используется тип группировки TOTAL. Этот тип группировки подходит для подсчета итоговых сумм, ранжирования строк и прочих операций, для которых нужна информация о всех исходных данных.
Пример
Необходимо вычислить среднюю сумму продаж (AvgSales) и отклонения от нее для каждой категории в городе (DeltaFromAvg). Для этого подойдет функция AVG:
- AvgSales —
AVG(SUM([Sales]) TOTAL) - DeltaFromAvg —
SUM([Sales]) - [AvgSales]

Группировка для нескольких окон
Иногда оконную функцию нужно вычислить не среди всех записей, а раздельно по группам. В этих случаях используются типы группировки WITHIN и AMONG.
WITHIN
WITHIN — это аналог GROUP BY в SQL. В нем перечисляются все измерения, по которым будет производиться разбиение на окна. Также в WITHIN можно использовать показатели. В этом случае их значения таким же образом будут участвовать в группировке окна.
Важно
В WITHIN игнорируются измерения, которые не участвуют в группировке чарта. Например, в чарте с группировкой по измерениям City и Category для показателя SUM(SUM([Sales]) WITHIN [Date]) измерение Date будет проигнорировано, и он станет равносилен показателю SUM(SUM([Sales]) TOTAL).
Пример
Вычисление доли каждой категории (% Total) от общей суммы продаж по городу (TotalSales):
- TotalSales —
SUM(SUM([Sales]) WITHIN [City]) - % Total —
SUM([Sales]) / [TotalSales]
Результат на примере чарта Столбчатая диаграмма:

AMONG
В этом случае разбиение на окна будет производиться по всем измерениям, которые участвуют в группировке чарта, но не перечислены в AMONG. Таким образом, этот тип группировки противоположен типу WITHIN. Во время вычисления функции AMONG трансформируется в WITHIN, который выполняет группировку по всем измерениям, которые не перечислены в AMONG.
Так, например, для чарта с группировкой по измерениям City и Category равносильны показатели:
SUM(SUM([Sales]) AMONG [Category])иSUM(SUM([Sales]) WITHIN [City])SUM(SUM([Sales]) AMONG [City], [Category])иSUM(SUM([Sales]) TOTAL)
Такой вариант записи существует исключительно для удобства и применяется в тех случаях, когда заранее неизвестно, в каких измерениях будет строиться чарт, но важно исключить из оконной группировки определенные измерения.
Важно
Измерения, перечисленные в AMONG, должны быть добавлены в секции чарта. В другом случае чарт вернет ошибку.
Сортировка
Некоторые оконные функции поддерживают сортировку, направление которой влияет на расчет значения. Указать сортировку для оконной функции можно следующими способами:
- указать измерения или показатели в секции
ORDER BY; - в чарте перенести измерения или показатели в секцию Сортировка.
Измерения и показатели для сортировки берутся сначала из секции ORDER BY в формуле, а затем из секции чарта Сортировка.
Пример
Необходимо рассчитать изменение общей суммы продаж (IncTotal) за весь период, начиная от самой ранней даты и заканчивая самой поздней. Для этого можно воспользоваться функцией RSUM с сортировкой по измерению Date — RSUM(SUM([Sales]) TOTAL ORDER BY [Date]).
Результат на примере чарта Линейная диаграмма:

Аналогичный результат можно получить, если задать показатель IncTotal формулой RSUM(SUM([Sales]) TOTAL) и добавить измерение Date в секцию Сортировка.
Фильтрация
Вычисление значений функций в чартах выполняется после применения фильтров по измерениям и показателям, добавленным в секцию Фильтры. У оконных функций можно переопределить этот порядок. Для это нужно указать необходимые измерения или показатели в секции BEFORE FILTER BY формулы. В таком случае значение функции будет вычислено до применения фильтрации.
Изменение порядка вычисления применяется в том случае, когда нужно рассчитать значение функции для исходного набора данных, но данные в чарте при этом ограничены фильтром.
Пример
Необходимо рассчитать изменение общей суммы продаж (IncTotal) за период с 17.01.2014 по 11.03.2014. Если добавить в чарт фильтр по измерению Date и создать показатель RSUM(SUM([Sales]) TOTAL ORDER BY [Date]), то функция будет рассчитана только для тех данных, которые ограничены фильтром:

Чтобы вычислить функцию для всего объема данных, но отобразить результат только в определенном периоде, необходимо добавить измерение Date в секцию BEFORE FILTER BY — RSUM(SUM([Sales]) TOTAL ORDER BY [Date] BEFORE FILTER BY [Date]).

Создание показателя для оконной функции
В качестве первого аргумента (value в описании синтаксиса) оконной функции нельзя напрямую использовать измерение. Сначала нужно применить к нему функцию агрегации, в результате чего измерение станет показателем, который можно использовать в оконной функции.
Например, в чарте с группировкой по измерениям Year и Category вы хотите ранжировать записи продаж по прибыли за весь период. Нельзя для этого использовать формулу RANK([Profit]), где Profit — измерение. Сначала надо применить функцию агрегации, чтобы перевести измерение Profit в показатель. Наиболее подходящей в данном случае будет агрегатная функция SUM, которая вернет сумму прибыли: SUM([Profit]). Теперь к полученному показателю можно применить оконную функцию ранжирования RANK. Итоговая корректная формула: RANK(SUM([Profit])).
Показатели можно добавлять как на уровне датасета, так и на уровне чарта. Подробнее см. Способы создания показателей.
Чтобы понять, какую именно агрегатную функцию выбрать для перевода измерения в показатель, следует уточнить, какой итоговый показатель вы хотите получить с помощью оконной функции. Например, в чарте с группировкой по категориям товаров (измерение Category) требуется упорядочить записи по продажам (измерение Sales). Если вы хотите упорядочить записи по сумме продаж, то следует выбрать агрегатную функцию SUM: SUM([Sales]), если по количеству продаж — COUNT: COUNT([Sales]).
Если требуется получить некоторый строковый показатель, значение которого будет определено путем группировки и сортировки в оконной функции, можно применить агрегатную функцию ANY.
Рассмотрим создание показателя для оконной функции на примерах. В качестве источника построим датасет на основе подключения к демонстрационной БД (таблица MS_SalesFullTable).
Пример 1
Необходимо показать количество продаж за день для каждой категории товаров и общее количество продаж за день.
-
Выберите тип чарта Таблица.
-
Добавьте в чарт поле
OrderDateс формулойDATE([OrderDatetime]). -
В секцию Столбцы поместите измерения
OrderDateиProductCategory. -
Чтобы упорядочить записи по дате продажи, поместите измерение
OrderDateв секцию Сортировка. -
Для подсчета количества продаж за день для каждой категории товаров добавьте в чарт показатель
cnt_order_date_category. Используйте агрегатную функцию COUNT. Она будет использовать группировку по измерениям, размещенным в секции Столбцы. Итоговая формула:COUNT([OrderID]). -
Поместите показатель
cnt_order_date_categoryв секцию Столбцы. -
Для подсчета общего количества продаж за день добавьте в чарт показатель
cnt_order_date. Используйте оконную функцию SUM, применив группировку по измерениюOrderDate. Чтобы перевести измерениеOrderIDв показатель, используйте агрегатную функцию COUNT:COUNT([OrderID]). Итоговая формула:SUM(COUNT([OrderID]) WITHIN [OrderDate]). -
Поместите показатель
cnt_order_date_categoryв секцию Столбцы.
Пример 2
Необходимо показать среднюю стоимость продаж по магазину и по категориям товаров в магазине:
-
Выберите тип чарта Таблица. В секцию Столбцы поместите измерения
ShopNameиProductCategory. -
Для подсчета средней стоимости продаж по категориям товаров в магазине добавьте в чарт показатель
avg_category_sale. Используйте агрегатную функцию AVG. Она будет использовать группировку по измерениям, размещенным в секции Столбцы. Итоговая формула:AVG([Sales]). -
Поместите показатель
avg_category_saleв секцию Столбцы. -
Для подсчета средней стоимости продаж по магазину добавьте в чарт показатель
avg_shop_sale. Используйте оконную функцию AVG, применив группировку по измерениюShopName. Чтобы перевести измерениеSalesв показатель, используйте агрегатную функцию AVG:AVG([Sales]). Итоговая формула:AVG(AVG([Sales]) WITHIN [ShopName]). -
Поместите показатель
avg_shop_saleв секцию Столбцы.
Пример 3
Необходимо вывести в сводную таблицу идентификатор последней за день продажи по магазинам:
-
Чтобы иметь возможность группировки данных по дате продажи (без учета времени), добавьте в чарт поле
Dateс формулойDATE_PARSE(STR([OrderDatetime])). -
Чтобы иметь возможность сортировки данных по времени продажи, добавьте в чарт поле
Timeс формулойRIGHT(STR([OrderDatetime]),8). -
Выберите тип чарта Сводная таблица. В секцию Строки поместите измерения
ShopName,OrderDatetimeиOrderID. -
Добавьте в чарт показатель
last_shop_order. Используйте оконную функцию LAST, применив группировку по измерениюShopNameи сортировку поTime. Для перевода строкового измерения в показатель используйте агрегатную функцию ANY с группировкойINCLUDE(для выдачи уникальных значений):ANY([OrderID] INCLUDE [OrderID]). Итоговая формула:LAST(ANY([OrderID] INCLUDE [OrderID]) WITHIN [ShopName], [Date] ORDER BY [Time]). -
Поместите показатель
last_shop_orderв секцию Показатели.
Вопросы и ответы
Как упорядочить значения при расчете нарастающего итога или скользящего среднего?
Для правильной работы функций, зависящих от порядка записей в окне (например, RSUM, MAVG, LAG, LAST, FIRST), обязательно указывать сортировку. Это можно сделать следующими способами:
- перетащить в секцию Сортировка измерение или показатель, по которым будет отсортирован весь чарт;
- задать сортировку для конкретной функции с помощью
ORDER BY.
Как правильно посчитать нарастающий итог после добавления поля в секцию Цвета?
В качестве примера рассмотрим линейную диаграмму, на которой построен график изменения общей суммы продаж по датам (см. таблицу Selling). Нарастающий итог (IncTotal) вычислен с использованием оконной функции RSUM — RSUM(SUM([Sales])).

Чтобы отобразить изменение суммы продаж по каждой категории товаров, нужно добавить измерение Category в секцию Цвета.

После этого на диаграмме отобразится отдельный график для каждой категории, но итоговые суммы на них посчитаны неверно: Furniture — 49 вместо 19, Office Supplies — 91 вместо 52, Technology — 42 вместо 20. Это связано с тем, что измерение в секции Цвета (Category) участвует в группировке так же, как и измерение в секции X (Date). Чтобы сумма посчиталась корректно, нужно добавить измерение Category в секцию WITHIN или измерение Date в секцию AMONG: RSUM(SUM([Sales]) WITHIN [Category]) или RSUM(SUM([Sales]) AMONG [Date]).

Как правильно вычислить оконную функцию, если в чарте указать группировку для даты?
При добавлении группировки (округления) для даты в чарте исходное поле подменяется на другое, автоматически сгенерированное. Например, при округлении до месяца вместо измерения [Date] подставляется новое поле с формулой DATETRUNC([Date], "month"). Так как исходное поле [Date] исчезает из перечня измерений чарта, то и оконная функция, в которой оно используется, перестает работать. Для корректной работы функции необходимо в формуле округлить исходное измерение [Date] с помощью функции DATETRUNC.