Представим ситуацию, что у нас есть таблица следующего вида:
В ней перечислены клиенты, которые подписались на наш продукт(условно клиент А и клиент В). В качестве дат указаны дата начала подписки и дата окончания. И нам необходимо построить что-то вроде диаграммы Ганта, на которой будет видно в хронологическом порядке активность клиентов
Т.е. по сути нам нужно создать даты из двух таким образом, чтобы на каждый день подписки была одна дата. Например, для клиента "А" с подпиской на журнал "За рулем" у нас должно получиться 115 строк дат, т.к. подписка длилась 115 дней(от 05.10.2020 до 27.01.2021). Проблема в том, что встроенных средств создания списка дат между двумя заданными датами в Power Query нет. Поэтому придется что-то изобретать.
Загружаем данные нашей таблицы в редактор Power Query: выделяем таблицу -вкладка Данные(Data)(или Power Query) -Получить и преобразовать данные(Get & Transform Data) -Из таблицы(From Table/Range).
После того как загрузили таблицу в Power Query следует убедиться, что тип данных в столбцах "Дата подписки" и "Дата отказа" именно дата, а не дата и время или тем более не тип любой или текст. Для этого на каждом столбце щелкаем левой кнопкой мыши по значку слева от заголовка и выбираем «Дата»(Date)
Или выделяем сразу оба столбца с датами -вкладка Главная(Home) -группа Преобразование(Transform) -раскрываем список Тип данных(Data type) и выбираем Дата(Data).
Далее, чтобы получить список дат для каждого клиента от одной даты до другой, необходимо использоваться функцию List. Идем на вкладку Добавление столбца(Add Column) -Настраиваемый столбец(Custom column). В качестве имени запишем «Даты», а в качестве формулы следующее:
Подробнее про создание вычисляемых столбцов и просмотр функций в PowerQuery можно ознакомиться в этой статье: Вычисления в PowerQuery List.Dates – функция языка M, которая берет начальную дату и прибавляет к ней указанное кол-во дней, и все это выводит в виде списка(тип List). Для определения кол-ва дней мы используем обычное вычитание дат([Дата отказа]-[Дата продажи]), преобразуя при этом полученную разность в тип Длительность при помощи функции Duration.From, а далее в количество дней при помощи Duration.Days. Если этого не сделать, то можем получить ошибку несовпадения типов. После этого задаем шаг в днях, который нужно использовать для создания списка - #duration(1, 0, 0, 0). #duration(кол-во дней, кол-во часов, кол-во минут, кол-во секунд)
Будет добавлен новый столбец, в котором для каждого товара каждого клиента будет создан список дат использования продукта. Остается лишь щелкнуть левой кнопкой мыши на значке разнонаправленных стрелок в заголовке созданного столбца и выбрать Развернуть в новые строки(Expand to New Rows):
Полученный столбец со всеми датами преобразуем в тип Дата(вкладка Главная(Home) -группа Преобразование(Transform) -раскрываем список Тип данных(Data type) и выбираем Дата(Data).
Если в дальнейшем мы планируем работать с результатом запроса в Excel через выгрузку в сводную таблицу(Главная(Home) -Закрыть и загрузить(Close & Load) -Отчет сводной таблицы(Pivot table report)), то на этом шаге можно сохранить и загрузить запрос и уже в сводной таблице просто поместить созданный столбец «Даты» в область Столбцов.
Но если мы хотим развернуть эти даты в столбцы непосредственно через PowerQuery, то для начала необходимо отсортировать столбец с датами по возрастанию: жмем на значок стрелки в правом верхнем углу столбца и из раскрывающегося списка выбираем Сортировка по возрастанию(Sort Ascending):
После чего используем столбец сведения: выделяем созданный столбец дат -вкладка Преобразование(Transform) -Столбец сведения(Pivot Column). В качестве Столбца значений(Values column) указываем столбец Дата подписки. Нажимаем кнопку Расширенные параметры(Advanced options) и в качестве Функции агрегированного значения(Aggregate Value Function) указываем Количество(все)(Count (All))
После этого можно удалить лишние столбцы (например, столбец «Дата отказа») и останется выгрузить данные на лист: Главная(Home) -Закрыть и загрузить(Close & Load) -Таблица(Table). Созданная таблица выгрузится на лист.
В загруженном листе нам останется сделать форматирование, чтобы отметить только те ячейки, в которых у клиентов были активные подписки. Выделяем ячейки со значениями 0 и 1(те, что под датами) -Главная(Home) -Условное форматирование(Conditional formatting) -Правила выделения ячеек(Highlight Cells Rules) -Равно(Equal To). Вписываем значение 1 и выбираем формат(если предустановленные форматы не подходят, то в раскрывающемся списке выбираем «Пользовательский формат»).
А чтобы нам единички и нули не портили картину, мы опять выделим все ячейки с нулями и единичками -Правая кнопка мыши -Формат ячеек(Format cells) -вкладка Число(Number). Идем в самый низ –(все форматы)(Custom). Выставляем в поле тип такой формат: ;;;
Теперь нули и единички видны не будут, а формат будет работать. В итоге добьемся нужного результата.
Но что делать, когда надо отследить подписку не по дням, а по месяцам? Самый большой тип длительности, который поддерживает функция duration – это дни. Поэтому придется хитрить. Здесь функция List.Dates нам уже не поможет, т.к. построена именно на возможностях функции duration. Но мы можем использовать более гибкую функцию: List.Generate. Она способна создавать практически любые списки.
Добавляем новый столбец(вкладка Добавление столбца(Add Column) -Настраиваемый столбец(Custom column)). В качестве формулы запишем:
let
start =[Дата продажи],end=[Дата отказа]inList.Generate(()=>[day=start],each[day]<=end,each[day=Date.AddMonths([day],1)],each[day])
let
start = [Дата продажи],
end = [Дата отказа]
in
List.Generate(
()=>[day=start],
each [day]<=end,
each [day=Date.AddMonths([day], 1)],
each [day]
)
А дальше все так же: щелкнуть левой кнопкой мыши на значке разнонаправленных стрелок -Развернуть в новые строки(Expand to New Rows). И при необходимости все это «перевернуть» в столбцы(алгоритм точно такой же, как описан выше для списка дат). Чуть подробнее про то, что мы записали в нашем созданном столбце: Let – здесь мы задаем дату начала и окончания для каждой строки списка. Можно сказать просто запоминаем их для дальнейшего использования. List.Generate – специальная функция, которой мы задаем начальное значение даты ( ()=>[day=start],); далее указываем условие по которому необходимо завершить цикл(each [day]<=end ) – т.е. на какой дате закончить создавать список; после этого указываем что именно делать внутри каждого шага цикла(each [day=Date.AddMonths([day], 1)]) – добавить один месяц и указываем, что проделывать это надо для каждого нового значения(each [day]).
Т.е. функция берет дату начала как точку отсчета([day=start]), прибавляет к ней указанный интервал(Date.AddMonths([day], 1)]) и так до тех пор, пока новая дата не станет меньше или равна конечной([day]<=end). И на каждом шаге новая дата добавляется в итоговый список.
На самом деле, для того, чтобы это использовать не надо особо вдумываться в процесс. Копи-паст никто не отменял 😊 Самое главное, на что здесь стоит обратить внимание, это функция Date.AddMonths. Она при каждом шаге цикла прибавляет к переменному значению day(которое начинается у нас с Даты продажи) по одному месяцу. Если нужен другой период(дни, недели, годы), то нужно просто подставить вместо AddMonths другую функцию. Вот возможные варианты:
Date.AddDays – добавляет дни
Date.AddWeeks – добавляет недели
Date.AddMonths – добавляет месяцы(что мы и делали)
Date.AddQuarters – добавляет кварталы
Date.AddYears – добавляет годы
ВАЖНО: Что надо помнить, если используем создание списка не под дням, а, например, по месяцам. Даты для месяцев у нас могут быть разные (например, в примере выше дата начала для клиента «А» 10.09.2020, а дата окончания - 07.02.2021). Это может привести к тому, что в каких-то строках могут пропасть последние месяцы. Решается это довольно просто: сразу после того, как загрузили данные и изменили тип столбцов на тип Дата(второй шаг запроса - перед добавлением пользовательского столбца), необходимо щелкнуть правой кнопкой мыши по столбцу дат -выбрать пункт Преобразование(Transform) -Месяц(Month) -Начало месяца(Start of Month). Это заставит нашу функцию вычислять интервалы по месяцам корректно. Точно такой же алгоритм и для других периодов: лет, недель, кварталов.