DAX or Power Query | Correct value of record based on related records within same table - dax

The problem I'm trying to solve is on a multi-doctor planner database. Due to human error & bad habits, 3 different appointment status indicate to the user that the appointment actually took place. Ufortunately, there is an exception, evident only to the user, that takes place whenever a patient has more than one appointment on the same day, with the same doctor.
Case in point, Fulano de Tal had a multi stage consult with Dr. Smith on the 9th, starting at 13:30 hrs. The first 2 sessions (APP_IDs 2 and 3) are easily indentified as completed, but the one at 14:30 hrs had to have taken place, or would have been outright marked as cancelled. The reason it is known that APP_ID 4 took place is that 2 and 3 were completed. Fulano did not arrive to app_id 5, because it was on the next day, and there was no previous engagement on that day that could be used as a reference.
On the other hand, Pedrito was supposed to have a 3 stage consult with Dr. Doe. Pedrito did not arrive to APP_ID 6, but he did arrive for 7 and 8. APP_ID 7 completion is evident, but we only know 8 did so, because it was scheduled on the same day, at a later hour, whereas APP_ID 6 was scheduled before the one we know for certain took place.
APP_ID
Planner ID
Patient
Date
Date_Time
System Status
Completed?
1
Dr. Smith
Juan Perez
09-dec-2022
09-dec-2022 12:00
Completed
YES
2
Dr. Smith
Fulano de Tal
09-dec-2022
09-dec-2022 13:00
In Consult
YES
3
Dr. Smith
Fulano de Tal
09-dec-2022
09-dec-2022 13:30
Waiting
YES
4
Dr. Smith
Fulano de Tal
09-dec-2022
09-dec-2022 14:00
Called Upon
should be YES
5
Dr. Smith
Fulano de Tal
10-dec-2022
10-dec-2022 14:30
Called Upon
NO
6
Dr. Doe
Pedrito
09-dec-2022
09-dec-2022 09:00
Called Upon
NO
7
Dr. Doe
Pedrito
09-dec-2022
09-dec-2022 09:30
Completed
YES
8
Dr. Doe
Pedrito
09-dec-2022
09-dec-2022 10:00
Called Upon
should be YES
What I need is a calculated column that returns YES whenever:
The status is either Completed, In Consult or Waiting (this is the easy part)
The status is Called Upon AND the patient already had an appointment whose status is one of the above AND it took place on the same day AND it took place at a later time.
I already tried it on Dax, using a calculated countrows, like in this post, and adding additional conditions within the filter. But I guess because powerbi sorts the table so as to optimize storage, the earlier() function does can't properly do a sweep based on dates and time. Therefore, the solution might lie at powerquery, where I can use table.buffer to forcefully sort the table, but what I outright don't know how to do is add the calculated column that makes the full sweep to check for the easy condition and the four less than easy ones.
A solution in either powerquery or with dax work for me.
Please, help me out.

M / Powerquery method
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Date", type date}, {"Date_Time", type datetime}}),
// x[] is current row, [] is entire table
#"Added Custom" = Table.AddColumn(#"Changed Type", "Custom",
(x)=>Table.RowCount(Table.SelectRows( #"Changed Type", each
[Patient]=x[Patient]
and ([System Status]="Completed" or [System Status]="In Consult" or [System Status]="Waiting")
and x[System Status]="Called Upon"
and [Date]=x[Date]
and [Date_Time]<x[Date_Time]))
),
#"Added Custom1" = Table.AddColumn(#"Added Custom", "Completed", each if [System Status]="Completed" or [System Status]="In Consult" or [System Status]="Waiting" or [Custom]>0 then "YES" else "NO"),
#"Removed Columns" = Table.RemoveColumns(#"Added Custom1",{"Custom"})
in #"Removed Columns"
EDITED VERISON
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
process=(z as table)=> let
#"ProcessTable" = Table.AddColumn(z, "Custom",
(x)=>Table.RowCount(Table.SelectRows( z, each
([System Status]="Completed" or [System Status]="In Consult" or [System Status]="Waiting")
and x[System Status]="Called Upon"
and [Date_Time]<x[Date_Time]))
) in #"ProcessTable",
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Date", type date}, {"Date_Time", type datetime}}),
#"Grouped Rows" = Table.Group(#"Changed Type", {"Patient", "Date"}, {{"data", each process(_), type table }}),
#"Expanded data" = Table.ExpandTableColumn(#"Grouped Rows", "data", {"APP_ID", "Planner ID", "Date_Time", "System Status", "Custom"}, {"APP_ID", "Planner ID", "Date_Time", "System Status", "Custom"}),
#"Added Custom1" = Table.AddColumn(#"Expanded data", "Completed", each if [System Status]="Completed" or [System Status]="In Consult" or [System Status]="Waiting" or [Custom]>0 then "YES" else "NO")
in #"Added Custom1"

#horseyride
In the end, I went for an auxiliary table. I made a reference query in powerquery, selecting only the states that guarantee the completion of each appointment, and the columns needed to relate the ambiguous consults in the main table.
Although the appointment IDs are not unique, they almost are. So, even though a calculated ditinct count proved to be too much for my machine, a countrows of a filtered table (by the relatable traits) proved quite fast.
Thank you #horseyride.

Related

Power Query Function - get column name

I have different files containing various attributes (one file will have Job Name, another one will have Location, another one will have Grade, etc.).
Sample Data:
Job Name
Job Location
Job Grade
Controller
Some values
High
Director
No Values
Low
Analyst
Some Other values
VP Of Finance
Blank
Medium
Director
I want to have a function that will add a column in which each cell will have combined: the Column Name (Job Name in case of the above sample) AND length of the Column Name (Length of each Job Name in case of the above sample).
Desired Output:
Job Name
Job Location
Job Grade
Desired Output
Controller
Some values
High
Job Name 10
Director
No Values
Low
Job Name 8
Analyst
Some Other values
Job Name 7
VP Of Finance
Blank
Medium
Job Name 13
Director
Executive
Job Name 8
What I have is:
(MyColumn) as text =>
let
Source = Text.Length(MyColumn),
NewSource = Number.ToText(Source),
FinalText = Text.Combine({MyColumn,NewSource}," ")
in FinalText
I need to find a way to reference the column name to be added to the FinalText, because for now the output is following:
Job Name
Current Output (incorrect!)
Engineer
Engineer 1
Director
Director 2
Analyst
Analyst 3
Engineer
Engineer 1
Btw I tried using Record.Field(_, MyColumn) but it didn't work with below error:
An error occurred in the ‘’ query. Expression.Error: The name '_' wasn't recognized. Make sure it's spelled correctly.
Thank you in advance for help.
Edit: I have edited the post, I hope it's more clear now.
Is this what you want?
zzz = Table.AddColumn(#"PriorStepNameHere", "Custom", each Table.ColumnNames(#"PriorStepNameHere"){0} &" "& Text.From(Text.Length(Record.Field(_,Table.ColumnNames(#"PriorStepNameHere"){0}))))
Here is a similar answer.
Table1 looks like this.
Job Name
Controller
Director
Analyst
VP Of Finance
Director
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Added Custom" = Table.AddColumn(Source, "Output", each Text.Combine({
Table.ColumnNames(Source){0}," ",
Text.From(Text.Length(Record.Field(_,Table.ColumnNames(Source){0})))
}))
in
#"Added Custom"
Edit 1 from comment:
Based on what you want I recommend that you don't use "Add Column>Invoke Custom Function". Instead in the "Applied Steps" right click and choose "Insert step after" you would then type in the =function_name(prior_step_table,number_of_column) the function is below.
(Table as table, column_n as number) =>
let
Source = Table,
#"Added Custom" = Table.AddColumn(Source, "Output", each Text.Combine({Table.ColumnNames(Source){column_n}," ",Text.From(Text.Length(Record.Field(_,Table.ColumnNames(Source){column_n})))}))
in
#"Added Custom"

Using the power query GUI to nest tables

powerquery: This follow-up question relates to;
Aggregation/Summation of text and numeric fields
Ron, could you please clarify, from your “group by” code statements what is the equivalent if completing in the GUI.
I can get close but not the same as yours, or is it not possible to nest tables in the GUI ?
The "group by" portion of your code delivers this,
https://i.stack.imgur.com/rHz1B.png
I would like to achieve the same via the GUI
Below is what I am ultimately trying to achieve using code as the GUI did not work out as planned.
https://i.stack.imgur.com/23naf.png
I have tried "table.group" nesting as follows
Site
------->Agency
-------------->Division
Site
------->Agency
------->Division
Site
------->Agency
Site
------->Division
But not quite exactly what I want. Any assistance would be greatly appreciated.
I really don't understand your reluctance to use the Advanced Editor, but here is a method of adding a bunch of custom columns, each with their own custom formula, to get the results you show.
Paste in the M code and change the Source as per the previous instructions.
Then you can double click on any of the added custom columns in the Applied Steps to see the custom formula that was used.
I renamed the steps so they would be easier to follow.
Any step with a 'gear' icon on the right can be opened to examine the associated dialog box.
let
Source = Excel.CurrentWorkbook(){[Name="Table15"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"SiteName", type text}, {"Agency", type text}, {"Division", type text}, {"Staff Numbers", Int64.Type}}),
#"Grouped Rows" = Table.Group(#"Changed Type", {"SiteName"},
{{"ALL", each _, type table [SiteName=nullable text, Agency=nullable text, Division=nullable text, Staff Numbers=nullable number]}}),
Agency = Table.AddColumn(#"Grouped Rows", "Agency", each Text.Combine(List.Distinct([ALL][Agency]),"/")),
#"Agency Staff Numbers Tbl" = Table.AddColumn(Agency, "Agency Staff Numbers tbl", each
Table.Group([ALL],"Agency", {"Agency Staff Numbers",each List.Sum([Staff Numbers])})),
#"Agency Staff Numbers" = Table.AddColumn(#"Agency Staff Numbers Tbl", "Agency Staff Numbers",
each Text.Combine(List.Transform([Agency Staff Numbers tbl][Agency Staff Numbers],each Text.From(_)),"/")),
#"Removed Columns" = Table.RemoveColumns(#"Agency Staff Numbers",{"Agency Staff Numbers tbl"}),
Division = Table.AddColumn(#"Removed Columns", "Division", each Text.Combine(List.Distinct([ALL][Division]),"/")),
#"Division Staff Tbl" = Table.AddColumn(Division, "Divison Staff Numbers Tbl", each
Table.Group([ALL],"Division", {"Division Staff Numbers",(t)=> List.Sum(t[Staff Numbers])})),
#"Division Staff Numbers" = Table.AddColumn(#"Division Staff Tbl", "Division Staff Numbers", each
Text.Combine(List.Transform([Divison Staff Numbers Tbl][Division Staff Numbers],each Text.From(_)),"/")),
#"Removed Columns1" = Table.RemoveColumns(#"Division Staff Numbers",{"Divison Staff Numbers Tbl", "ALL"}),
#"Changed Type1" = Table.TransformColumnTypes(#"Removed Columns1",{
{"Agency", type text}, {"Agency Staff Numbers", type text}, {"Division", type text}, {"Division Staff Numbers", type text}
})
in
#"Changed Type1"
I also added another site for debugging purposes:

Power Query M - Group by Column Value with Custom Aggregation (Percentile)

I am trying to calculate percentiles by group (from column values ex: hours by department, sales by region, etc.) within power query. This same logic could be used for other custom group aggregation. After lots of searching, I found 2 potential approaches.
Approach 1:
this archived article which looked to have the perfect answer. Nothing else I could find comes close.
The solution from there is the following custom function:
//PercentileInclusive Function
(inputSeries as list, percentile as number) =>
let
SeriesCount = List.Count(inputSeries),
PercentileRank = percentile * (SeriesCount - 1) + 1, //percentile value between 0 and 1
PercentileRankRoundedUp = Number.RoundUp(PercentileRank),
PercentileRankRoundedDown = Number.RoundDown(PercentileRank),
Percentile1 = List.Max(List.MinN(inputSeries, PercentileRankRoundedDown)),
Percentile2 = List.Max(List.MinN(inputSeries, PercentileRankRoundedUp)),
PercentileInclusive = Percentile1 + (Percentile2 - Percentile1) * (PercentileRank - PercentileRankRoundedDown)
in
PercentileInclusive
Combined with a step in your table to group appropriately and use the function:
=Table.Group(TableName, {"Grouping Column"}, {{"New Column name", each
PercentileInclusive(TableName[Column to calculate Percentile of], percentile # between 0 and 1)}})
[edited to correct the typo Ron R. pointed out and remove unnecessary detail]
Example input:
Pen Type
Units Sold
Ball-Point
6,109
Ball-Point
3,085
Ball-Point
1,970
Ball-Point
8,190
Ball-Point
6,006
Ball-Point
2,671
Ball-Point
6,875
Roller
778
Roller
9,329
Roller
7,781
Roller
4,182
Roller
2,016
Roller
5,785
Roller
1,411
Desired output for a 25% inclusive percentile grouped by Pen Type:
Pen Type
0.25 Inclusive Percentile (Correct)
Ball-Point
2,878
Roller
1,714
Notes: No decimals shown above, calculated with Excel's PERCENTILE.INC function.
Approach 1 works great.
Approach 2:
Here is an alternate Power Query solution I tried. It is a single step with no custom function. It seems like it should do the trick, but I can't figure out a way to make the conditional check be row based. Something needs to go where I have //Condition// that tells it which rows belong in the current rows group, but no matter what I try it does not work. It either breaks, or gives a percentile for everything, ignoring the grouping.
=List.Percentile(Table.Column(Table.SelectRows(#"Previous Step Name", //Condition//), "Column to calculate percentile of"), percentile # 0 to 1)
Any ideas how to make approach 2 work?
It appears your Table.Group function is incorrectly specified.
Where my previous step was #"Changed Type", the following works:
#"Grouped Rows" = Table.Group(#"Changed Type", {"Pen Type"}, {
{"Percentile", each fnPercentileINC([Units Sold],0.25)}})
Original Data
M Code
let
Source = Excel.CurrentWorkbook(){[Name="Table4"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Pen Type", type text}, {"Units Sold", Int64.Type}}),
#"Grouped Rows" = Table.Group(#"Changed Type", {"Pen Type"}, {
{"Percentile", each fnPercentileINC([Units Sold],0.25), type number}})
in
#"Grouped Rows"
Result
Edit:
For your approach #2, without a custom function, you can merely use List.Percentile as an aggregation in the Table.Group function:
#"Grouped Rows" = Table.Group(#"Changed Type", {"Pen Type"}, {
{"25th Percentile", each List.Percentile([Units Sold],0.25)}
})

Can I add a new column with Linear Interpolation in Power Query M?

I am working on extracting an Interest Rate curve from futures market prices and create a table (Table 1) inside power query with the following columns:
- BusinessDays: Represents the nr o business days from today to the expiry of each future contract
- InterestRate: Represents the rate from today until the expiry of the futures contract
The second table (table 2) refers to the ID of internal financial products that expire in different business days.
- InstrumentID: Unique internal ID a financial product selled by a financial institution
- BusinessDays: Represents the nr o business days from today to the expiry of each financial product
I am having some trouble with M language, and unfortunately this specific calculation must be executed in Excel, so i am restricted to Power Query M.
The specific step i am not able to do is:
Creating a function in power query that adds a new column do table 2 containing the interpolated interest rate os each financial product.
The end result i am looking for would look like this
There are several ways to approach this but one way or another, you'll need to do some kind of lookup to determine which bracket to match your BusinessDays value with, so you can calculate the interpolated value.
I think it's simpler to just generate an all inclusive list of days vs interest rates, and then do a Join to pull out the matches.
I Name'd this first query intRates and expanded the Interest Rate table:
let
//Get the interest rate/business day table
Source = Excel.CurrentWorkbook(){[Name="intRates"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"BusinessDays", Int64.Type}, {"InterestRate", Percentage.Type}}),
//Add two columns which are the interest rate and business day columns offset by one
//It is faster to subtract this way than by adding an Index column
offset=
Table.FromColumns(
Table.ToColumns(#"Changed Type")
& {List.RemoveFirstN(#"Changed Type"[BusinessDays]) & {null}}
& {(List.RemoveFirstN(#"Changed Type"[InterestRate])) & {null}},
type table[BusinessDays=Int64.Type, InterestRate=Percentage.Type, shifted BusDays=Int64.Type, shifted IntRate=Percentage.Type]),
//Add a column with a list of the interest rates for each data interpolated between the segments
#"Added Custom" = Table.AddColumn(offset, "IntList", each let
sbd=[shifted BusDays],
intRateIncrement = ([shifted IntRate]-[InterestRate])/([shifted BusDays]-[BusinessDays]),
Lists= List.Generate(
()=>[d=[BusinessDays],i=[InterestRate]],
each [d]< sbd,
each [d=[d]+1, i = [i]+intRateIncrement],
each [i])
in Lists),
//add another column with a list of days corresponding to the interest rates
#"Added Custom1" = Table.AddColumn(#"Added Custom", "dayList", each {[BusinessDays]..[shifted BusDays]-1}),
//remove the last row as it will have an error
remErrRow = Table.RemoveLastN(#"Added Custom1",1),
//create the new table which has the rates for every duration
intRateTable = Table.FromColumns(
{List.Combine(remErrRow[dayList]),List.Combine(remErrRow[IntList])},
type table[Days=Int64.Type, Interest=Percentage.Type])
in
intRateTable
This results in a table that has every day (from 39 to , with its corresponding interest rate.
Then read in the "Instruments" table and Join it with the intRates, using a JoinKind.LeftOuter
let
Source = Excel.CurrentWorkbook(){[Name="Instruments"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"InstrumentID", type text}, {"BusinessDays", Int64.Type}}),
//add the rate column
#"Merged Queries" = Table.NestedJoin(#"Changed Type", {"BusinessDays"}, intRates, {"Days"}, "intRates", JoinKind.LeftOuter),
#"Expanded intRates" = Table.ExpandTableColumn(#"Merged Queries", "intRates", {"Interest"}, {"Interest"})
in
#"Expanded intRates"
Some of the results in the middle part of the table differ from what you've posted, but seem to be consistent with the linear interpolation formula for between two values, so I'm not sure how the discrepancy arises

power query subtract row below from row above using multiple conditions

I am using Power Query in Excel and I need to calculate the duration on each "Door_side" using the Time column on a daily level for each individual user.
The data comes from a card based access system and is formatted as follows:
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
I tried the following:
Sorted the Rows by Date, User and Time;
Added Index column;
Created Custom column named PreviousTime;
Calculated Duration (Time - PreviousTime).
The full code for the above mentioned steps is:
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
This works on small data sets but causes functionality issues and completely fails on a larger amount of data.
I am fairly new to Power Query and M so I just can't figure out what exactly from the custom column formula causes issues or how to approach this in another way.
I tried to keep the above code as part of my query and also to use it as a function but there is not much difference functionality wise between these two approaches.
The processed table will be sent to the Data Model but I was hoping to obtain the duration in Power Query rather than in Power Pivot.
A big thank you in advance!
To detail on the task a bit more I uploaded a reduced version of the data, for 3 users for the month of December. You can find it here: https://1drv.ms/x/s!AocQlL_KAzymgwhqiKxSL5JMZheL.
What I want to achieve is to calculate the duration between the timestamps based on user and date.
As a plus I do not have users working past midnight so all timestamps for a specific shift will be within the same date.
An example of the desired outcome can be found within the workbook as well and looks like this (calculated in 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
Because the data contains all users and multiple days I am attempting to do the calculations within tables grouped by Date and User.
I spent some time testing all 3 approaches presented below (List.Min, Table.FirstN & nested tables) and on a limited data set all of them do a great job.
However, when applied to a larger dataset (I have around 20000 rows for 1 month) the nested tables approach seems to be the fastest.
Thank you Eugene and Marc for helping and, more important, for teaching me something new.
Here's a different approach. It relies on working in nested tables.
I started with your data from your spreadsheet, in a table named Table1:
In Power Query, using Table1 as the source, I split the Booking Time column, renamed the resulting date and time columns, filtered, out the - Doorside entries, and sorted per your guidance:
Then I grouped by Booking Date and User:
Then I added an index column within each of the nested tables, in a new custom column:
Then I added a new column with the previous time within each of the nested tables, in a new custom column:
(The error you see here is because there is no previous time.)
Then I added a new column with the corrections for the errors from when I added the previous date, in each of the nested tables, in a new custom column. I figured I would "correct" caused by no previous times, by replacing the error with the "current" Booking Time, which would result in a duration of zero:
Then I added a new column with the durations calculated in each of the nested tables, in a new custom column:
Then I removed all columns except the last one I had added, which I had called AddDuration:
Then I expanded the AddDuration column:
Here's my M code:
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"
If I got your task correctly, you need time when next event occured, presuming this is the time door was closed.
In this case I strongly recommend you avoid using index. Instead, I suggest you to think out how to apply row selection procedure to gt what you need for each row.
Here is what I think should work if my understanding of your task was correct:
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
In GetCloseTime step I add function column, which selects rows from the table self, with the same date and later in time, and then selects minimal time. This will be next event time. You can add additional criteria if you need.
Another way is instead using List.Min make a sorted derived table and take its 1st row and value in column 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

Resources