запрос мощности вычитает строку ниже из строки выше, используя несколько условий

Я использую Power Query в Excel, и мне нужно рассчитать продолжительность для каждой «Door_side», используя столбец «Время» на ежедневном уровне для каждого отдельного пользователя.

Данные поступают из системы доступа на основе карты и имеют следующий формат:

Date  Time   User_No   Door_side
03/12  08:59   User_05   Outside
03/12  09:00   User_33   Inside
03/12  09:01   User_10   Outside
03/12  09:01   User_04   Outside
03/12  09:02   User_26   Outside
03/12  09:03   User_19   Outside
03/12  09:03   User_15   Inside
03/12  09:04   User_31   Inside
03/12  09:05   User_31   Outside
03/12  09:06   User_15   Outside
03/12  09:06   User_06   Inside
03/12  09:06   User_06   Inside
03/12  09:06   User_06   Inside
03/12  09:08   User_32   Outside
03/12  09:09   User_10   Inside
03/12  09:09   User_13   Inside
03/12  09:10   User_10   Outside

Я пробовал следующее:

  1. Сортировка строк по дате, пользователю и времени;
  2. Добавлен столбец индекса;
  3. Создан настраиваемый столбец с именем PreviousTime;
  4. Расчетная продолжительность (время - предыдущее время).

Полный код для вышеупомянутых шагов:

    let
    Source = Table,
     #"Sorted Rows" = Table.Sort(Source,{{"Date", Order.Ascending}, {"User_No", Order.Ascending}, {"Time", Order.Ascending}}),
    #"Added Index" = Table.AddIndexColumn(#"Sorted Rows", "Index", 0, 1),
    #"Added Custom" = Table.AddColumn(#"Added Index", "PreviousTime", each try 
if List.AllTrue(
{[User_No]=#"Added Index"[User_No]{[Index]-1},[Date]=#"Added Index"[Date]{[Index]-1}
}
)
then try #"Added Index"[Time]{[Index]-1} otherwise [Time]
else [Time]
otherwise [Time]),
    Duration = Table.AddColumn(#"Added Custom", "Duration", each [Time] - [PreviousTime], type duration)
in
    Duration

Это работает с небольшими наборами данных, но вызывает проблемы с функциональностью и полностью не работает с большим объемом данных. Я новичок в Power Query и M, поэтому я просто не могу понять, что именно из формулы настраиваемого столбца вызывает проблемы или как подойти к этому по-другому.

Я попытался сохранить приведенный выше код как часть моего запроса, а также использовать его как функцию, но между этими двумя подходами нет большой разницы в функциональности. Обработанная таблица будет отправлена ​​в модель данных, но я надеялся получить продолжительность в Power Query, а не в Power Pivot. Заранее большое спасибо!


Чтобы подробнее рассказать о задаче, я загрузил сокращенную версию данных для 3 пользователей за декабрь. Вы можете найти его здесь: https://1drv.ms/x/s!AocQlL_KAzymgwhqiKxSL5JMZheL.

Я хочу рассчитать продолжительность между отметками времени на основе пользователя и даты. В качестве плюса у меня нет пользователей, работающих после полуночи, поэтому все временные метки для определенной смены будут в пределах одной даты.

Пример желаемого результата также можно найти в книге и выглядит следующим образом (рассчитывается в Excel):

Date    Time    User    Door_side    Duration
03/12   06:54   User_1  Outside 
03/12   07:26   User_1  Inside    00:32:00
03/12   07:27   User_1  Outside   00:01:00
03/12   07:44   User_1  Inside    00:17:00
03/12   07:52   User_1  Outside   00:08:00
03/12   08:35   User_1  Inside    00:43:00
03/12   08:36   User_1  Outside   00:01:00
03/12   11:50   User_1  Inside    03:14:00
03/12   12:01   User_1  Outside   00:11:00
03/12   13:27   User_1  Inside    01:26:00
03/12   13:43   User_1  Outside   00:16:00
03/12   14:57   User_1  Inside    01:14:00
03/12   15:20   User_1  Inside    00:23:00
03/12   15:26   User_1  Outside   00:06:00
03/12   15:34   User_1  Inside    00:08:00

Поскольку данные содержат всех пользователей и несколько дней, я пытаюсь выполнить вычисления в таблицах, сгруппированных по дате и пользователю.


Я потратил некоторое время на тестирование всех 3 подходов, представленных ниже (List.Min, Table.FirstN и вложенные таблицы), и на ограниченном наборе данных все они отлично справляются со своей задачей.

Однако при применении к большему набору данных (у меня около 20000 строк за 1 месяц) подход с вложенными таблицами кажется самым быстрым.

Спасибо Евгению и Марку за помощь и, что более важно, за то, что научили меня чему-то новому.


person Andrea    schedule 22.02.2019    source источник
comment
Этот другой вопрос может быть вам полезен.   -  person Alexis Olson    schedule 22.02.2019


Ответы (2)


Вот другой подход. Он полагается на работу во вложенных таблицах.

Я начал с ваших данных из вашей электронной таблицы в таблице с именем Table1:  введите описание изображения здесь

В Power Query, используя Table1 в качестве источника, я разделил столбец Booking Time, переименовал полученные столбцы даты и времени, отфильтровал, исключил записи Doorside и отсортировал их в соответствии с вашими указаниями:  введите описание изображения здесь

Затем я сгруппировал по дате бронирования и пользователю:  введите описание изображения здесь < img src = "https://i.stack.imgur.com/qFysD.png" alt = "введите описание изображения здесь">

Затем я добавил столбец индекса в каждую из вложенных таблиц в новый настраиваемый столбец: введите описание изображения здесь  введите описание изображения здесь

Затем я добавил новый столбец с предыдущим временем в каждую из вложенных таблиц в новый настраиваемый столбец: введите описание изображения здесь  введите описание изображения здесь (Ошибка, которую вы видите здесь, связана с отсутствием предыдущего времени.)

Затем я добавил новый столбец с исправлениями ошибок с момента добавления предыдущей даты в каждую из вложенных таблиц в новый настраиваемый столбец. Я решил, что "исправлю" возникновение, вызванное отсутствием предыдущего раза, заменив ошибку "текущим" временем бронирования, что приведет к нулевой продолжительности:  введите описание изображения здесь < / a>  введите описание изображения здесь

Затем я добавил новый столбец с продолжительностью, рассчитанной в каждой из вложенных таблиц, в новый настраиваемый столбец: введите описание изображения здесь  введите описание изображения здесь

Затем я удалил все столбцы, кроме последнего, который я добавил, который я назвал AddDuration: введите описание изображения здесь

Затем я расширил столбец AddDuration:  введите описание изображения здесь

Вот мой M-код:

let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Split Column by Delimiter" = Table.SplitColumn(Table.TransformColumnTypes(Source, {{"Booking time", type text}}, "en-US"), "Booking time", Splitter.SplitTextByEachDelimiter({" "}, QuoteStyle.Csv, false), {"Booking time.1", "Booking time.2"}),
#"Renamed Columns" = Table.RenameColumns(#"Split Column by Delimiter",{{"Booking time.1", "Booking Date"}, {"Booking time.2", "Booking Time"}}),
#"Changed Type" = Table.TransformColumnTypes(#"Renamed Columns",{{"Booking Date", type date}, {"Booking Time", type time}}),
#"Filtered Rows" = Table.SelectRows(#"Changed Type", each ([Doorside] <> "-")),
#"Sorted Rows" = Table.Sort(#"Filtered Rows",{{"Booking Date", Order.Ascending}, {"User", Order.Ascending}, {"Booking Time", Order.Ascending}}),
#"Grouped Rows" = Table.Group(#"Sorted Rows", {"Booking Date", "User"}, {{"AllData", each _, type table}}),
#"Added Custom" = Table.AddColumn(#"Grouped Rows", "AddIndex", each Table.AddIndexColumn([AllData],"Index",0,1)),
#"Added Custom1" = Table.AddColumn(#"Added Custom", "AddPreviousTime", each let tblName = [AddIndex] in Table.AddColumn([AddIndex],"Previous Time",each tblName{[Index]-1}[Booking Time], type time)),
#"Added Custom2" = Table.AddColumn(#"Added Custom1", "CorrectErrors", each Table.ReplaceErrorValues([AddPreviousTime], {{"Previous Time", [AddPreviousTime][Booking Time]{0}}})),
#"Added Custom3" = Table.AddColumn(#"Added Custom2", "AddDuration", each Table.AddColumn([CorrectErrors],"Duration", each [Booking Time] - [Previous Time], type duration)),
#"Removed Other Columns" = Table.SelectColumns(#"Added Custom3",{"AddDuration"}),
#"Expanded AddDuration" = Table.ExpandTableColumn(#"Removed Other Columns", "AddDuration", {"Booking Date", "Booking Time", "User", "Doorside", "Index", "Previous Time", "Duration"}, {"Booking Date", "Booking Time", "User", "Doorside", "Index", "Previous Time", "Duration"})
in
#"Expanded AddDuration"
person Marc Pincince    schedule 28.02.2019

Если я правильно понял вашу задачу, вам нужно время, когда произойдет следующее событие, предполагая, что это время, когда дверь была закрыта. В этом случае я настоятельно рекомендую избегать использования index. Вместо этого я предлагаю вам подумать, как применить процедуру выбора строки к тому, что вам нужно для каждой строки.

Вот что, на мой взгляд, должно сработать, если я правильно понимаю вашу задачу:

let
    Source = Excel.CurrentWorkbook(){[Name="Data"]}[Content],
    SplitDateTime = Table.SplitColumn(Table.TransformColumnTypes(Source, {{"Booking time", type text}}, "en-GB"), "Booking time", Splitter.SplitTextByDelimiter(" ", QuoteStyle.Csv), {"Date", "Time"}),
    FilteredDoorside = Table.SelectRows(SplitDateTime, each ([Doorside] <> "-")),
    ChangedType = Table.Buffer(Table.TransformColumnTypes(FilteredDoorside,{{"Date", type date}, {"Time", type time}, {"User", type text}, {"Doorside", type text}})),
    GetCloseTime = Table.AddColumn(ChangedType, "Duration", (row)=>List.Min(Table.SelectRows(ChangedType, each [Date]=row[Date] and [Time]>row[Time])[Time]) - row[Time]),
    SetType = Table.TransformColumnTypes(GetCloseTime,{{"Duration", type duration}})
in
    SetType

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

Другой способ - вместо этого использовать List.Min создать отсортированную производную таблицу и взять ее 1-ю строку и значение в столбце Time: {0}[Time]

let
    Source = Excel.CurrentWorkbook(){[Name="Data"]}[Content],
    SplitDateTime = Table.SplitColumn(Table.TransformColumnTypes(Source, {{"Booking time", type text}}, "en-GB"), "Booking time", Splitter.SplitTextByDelimiter(" ", QuoteStyle.Csv), {"Date", "Time"}),
    FilteredDoorside = Table.SelectRows(SplitDateTime, each ([Doorside] <> "-")),
    ChangedType = Table.Buffer(Table.TransformColumnTypes(FilteredDoorside,{{"Date", type date}, {"Time", type time}, {"User", type text}, {"Doorside", type text}})),
    GetCloseTime = Table.AddColumn(ChangedType, "Duration", (row)=>Table.FirstN(Table.Sort(Table.SelectRows(ChangedType, each [Date]=row[Date] and [Time]>row[Time]),{{"Time", Order.Ascending}}),1){0}[Time] - row[Time]),
    SetType = Table.TransformColumnTypes(GetCloseTime,{{"Duration", type duration}})
in
    SetType
person Eugene    schedule 26.02.2019
comment
Спасибо за свежий взгляд, Юджин, после некоторых исследований я обнаружил, что столбцы индекса действительно вызывают проблемы с функциональностью при работе с большими наборами данных. Я отредактировал исходный пост для удобства обзора, добавив несколько дополнительных деталей о задаче, а также более крупный набор данных. Однако мне сложно реализовать ваш подход в сгруппированных таблицах. Вы бы подошли к этому сценарию иначе, пропустив группировку? - person Andrea; 27.02.2019
comment
@Andrea Я отредактировал свой ответ. На самом деле для запроса в вашем файле достаточно сделать шаг GetCloseTime из моего предыдущего ответа, чтобы он заработал. Сейчас я отредактировал ваш запрос и включил его в ответ в двух вариантах. - person Eugene; 27.02.2019