我正在Excel中使用Power Query,我需要使用“时间”列每天为每个用户计算每个“ 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
我尝试了以下操作:
上述步骤的完整代码为:
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中获得持续时间。 提前非常感谢您!
要详细说明该任务,我上载了12月份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和嵌套表),并且在有限的数据集上,它们都表现出色。
但是,当将其应用于较大的数据集(我有1个月约有20000行)时,嵌套表方法似乎是最快的。
谢谢Eugene和Marc的帮助,更重要的是,感谢我教给我一些新的知识。
答案 0 :(得分:0)
如果我正确地完成了任务,那么您需要等待下一个事件发生的时间,假设这是关门的时间。 在这种情况下,我强烈建议您避免使用索引。相反,我建议您考虑如何将行选择过程应用于gt每行所需的内容。
如果我对您的任务的理解正确,那么我认为这应该起作用:
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
步骤中,我添加了功能列,该列从表自身中选择具有相同日期和较晚时间的行,然后选择最短时间。这是下一个活动时间。您可以根据需要添加其他条件。
另一种方法是使用List.Min
创建一个排序的派生表,并在时间列{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
答案 1 :(得分:0)
这是另一种方法。它依靠在嵌套表中工作。
我从您的电子表格中的数据开始,该数据来自名为Table1的表:
在Power Query中,使用Table1作为源,我拆分了Booking Time列,将重命名的日期和时间列重命名,进行了过滤,在-Doorside条目中进行了过滤,并根据您的指南进行了排序:
然后,我在每个嵌套表的新自定义列中添加了上一次的新列: (您在这里看到的错误是因为没有上次时间。)
然后,我在新的自定义列中的每个嵌套表中添加了一个新列,其中包含对自我添加上一个日期以来的错误的更正。 我认为我可以“纠正”以前没有的错误,方法是将错误替换为“当前”预订时间,这将导致持续时间为零:
然后,我在新的自定义列中添加了一个新列,其中包含在每个嵌套表中计算的持续时间:
然后,我删除了除我添加的最后一列(我称为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"