Power Pivot - how to unpivot multiple colums, common months - excel

I have some data in the following format
Item
Spend Month 1
Spend Month 2
Income Month 1
Income Month 2
First
row
Number
Number
Number
Second
row
Number
Number
Number
I would like to trans form this to
Item
Month
Spend
Income
First
Month 1
Number
Number
First
Month 2
Number
Number
Second
Month 1
Number
Number
Second
Month 2
Number
Number

Here is one possible solution in PowerQuery:
let
Quelle = Excel.CurrentWorkbook(){[Name="Tabelle1"]}[Content],
UnpivotOtherColumns = Table.UnpivotOtherColumns(Quelle, {"Item"}, "Month", "Wert"),
DuplicateColumn = Table.DuplicateColumn(UnpivotOtherColumns, "Month", "Attribute"),
TextAfterDelim = Table.TransformColumns(DuplicateColumn, {{"Month", each Text.AfterDelimiter(_, " ", {1, RelativePosition.FromEnd}), type text}}),
TextBeforeDelim = Table.TransformColumns(TextAfterDelim, {{"Attribute", each Text.BeforeDelimiter(_, " "), type text}}),
PivotColumn = Table.Pivot(TextBeforeDelim, List.Distinct(TextBeforeDelim[Attribute]), "Attribute", "Wert")
in
PivotColumn

Related

Using Power query to group patient visits within a date range

I have a list of patients with visit effective from dates that fall within the effective dates of their initial visits that i don't need to bill. The effective dates start on the date of admission and end 30 days from the date of discharge. Since most patients are discharged the same day the common effective date rand is 30 days but can be more.
Patient
Visit start date
discharge + 29 days
Number of visits
Bill / Don't Bill
John
1/7/2021
2/5/2021
4
Bill
John
1/13/2021
2/11/2021
4
Don't Bill
John
2/11/2021
3/12/2021
4
Bill
John
2/18/2021
3/19/2021
4
Don't Bill
Jane
4/19/2021
5/18/2021
4
Bill
Jane
9/8/2021
10/7/2021
4
Bill
Jane
9/10/2021
10/9/2021
4
Don't Bill
Jane
9/18/2021
10/17/2021
4
Don't Bill
Joe
1/9/2021
2/7/2021
2
Bill
Joe
1/14/2021
2/12/2021
2
Don't Bill
I was hoping to find a function that can grab the initial date range based on the minimum of the "visit start date" column for each patient. In the image above the initial visit is marked "bill" and the initial date range is set to 1/7/2021-2/5/2021. Since John's 2nd visit has a visit start date that falls within the initial range it id marked don't bill. it does not matter that the discharge date is out of the range as long as the start date is within. John's 3rd visit has a visit start date outside the previous date range so it should be billed and set as the new date range. I hope this makes sense :(
enter image description here
Using PowerQuery (data ... from table/range .... )
The main trick is to sort on patient, then start date, and then offset the data one row so you can compare to what is in there already to see if it falls into the range of the prior row
Sample code and data, that you could paste into home... advanced editor...
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Patient", type text}, {"Visit start date", type date}}),
#"Sorted Rows" = Table.Sort(#"Changed Type",{{"Patient", Order.Ascending}, {"Visit start date", Order.Ascending}}),
//copy down all the columns, offset by one row
MinusOne = #table({"Column1"}, {{null}}) & Table.Skip(Table.DemoteHeaders(Table.RemoveLastN(#"Sorted Rows",1)),1),
custom1 = Table.ToColumns(#"Sorted Rows") & Table.ToColumns(MinusOne ),
custom2 = Table.FromColumns(custom1,Table.ColumnNames(#"Sorted Rows")&Table.ColumnNames(MinusOne ) ),
//start using them
#"Added Custom1" = Table.AddColumn(custom2, "Custom", each if [Column2]=null then [Visit start date] else if [Patient]=[Column1] and [Visit start date]>=[Column2] and [Visit start date]<=Date.AddDays([Column2],28) then [Column2] else [Visit start date]),
#"Added Custom" = Table.AddColumn(#"Added Custom1", "Bill / Dont Bill", each if [Visit start date]=[Custom] then "Bill" else "Don't Bill"),
#"Removed Columns" = Table.RemoveColumns(#"Added Custom",{"Column1", "Column2", "Custom"})
in #"Removed Columns"
If you need the bill end date, just add column .. custom column .. with formula =Date.Add([Visit Start Date],28)

How to extract months with data and find n-th value as starting point and n-th value as ending point in Excel Power Query, maybe VBA

I have a data set which consists of Date/Time, Pressure and Custom Column. This represents pressure over time data, where I wanna know my starting point (after 5 minutes) and ending point of -before last value (row) within one month. To help you a bit out, usually the measurements are taking roughly 30-40 mins what you can see on this example down. So it means the amount of data can vary.
The Time column is calculated using:
=([#[Date/Time]]-I5)*1440+L5
This data set represents whole data and all the months with values, and I need separated (filtered) months with these starting/ending points as on the screenshot. I used Power Query a lot to play with data, but maybe there is another method to obtain those values...and make them dynamic when possible for future data.
I will also upload my dummy workbook with whole data set (all the months), filter table with months if needed for your infos and test.
https://docs.google.com/spreadsheets/d/1LGl-eri6ewCni2NJ2wGeoYIf-40KO2Lr/edit?usp=sharing&ouid=101738555398870704584&rtpof=true&sd=true
In Power Query:
Based on your shared workbook and what you have written, it seems that for any given month, you
edit: minor change in algorithm
start the minute count after excluding the first entry in the month.
If that is a typo/error, just remove the function that removes that first line
with that second entry = minute 0, return the first entry in or after minute 5 as well as the next to last entry in the table.
Note that I started with just the Date and Pressure columns
Algorithm
Add a column of monthYear
GroupBy monthYear
Custom aggregation to
Remove the first and last rows of the table
Create a list of durations in minutes of each time compared with the first time in month. This will be a minute + fraction of a minute
Add that list as a column to the original table
Determine the first entry in or after the fifth minute
Determine the last entry
Filter the month subtable to return those two entries.
If you want to see the result for just a given month, you can filter the result in the resultant Excel table.
M Code
please read the comments and examine the Applied Steps to better understand the algorithm
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Date/Time", type datetime}, {"P7 [mbar]", Int64.Type}}),
//add month/year column for grouping
#"Added Custom" = Table.AddColumn(#"Changed Type", "month Year",
each Number.ToText(Date.Month([#"Date/Time"]),"00") & Number.ToText(Date.Year([#"Date/Time"]),"0000")),
#"Grouped Rows" = Table.Group(#"Added Custom", {"month Year"}, {
//elapsed minutes column
{"Elapsed Minutes", (x)=> let
//remove first and last rows from table
t=Table.RemoveColumns(Table.RemoveFirstN(Table.RemoveLastN(x)),"month Year"),
//add a column with the elapsed minutes
TableToFilter = Table.FromColumns(
Table.ToColumns(t)
& {List.Generate(
()=>[em=null, idx=0],
each [idx]< Table.RowCount(t),
each [em=Duration.TotalMinutes(t[#"Date/Time"]{[idx]+1} - t[#"Date/Time"]{0}), idx=[idx]+1],
each [em])}, type table[#"Date/Time"=datetime, #"P7 [mbar]"=number, elapsed=number]),
//filter for last entry (which would be next to last in the month
maxMinute = List.Max(TableToFilter[elapsed]),
//filter for first entry in the 5th minute
fifthMinute = List.Select(TableToFilter[elapsed], each Number.IntegerDivide(_,1)>=5){0},
//select the 5th minute and the last row
FilteredTable = Table.SelectRows(TableToFilter, each [elapsed]=fifthMinute or [elapsed]=maxMinute)
in FilteredTable,type table[#"Date/Time"=datetime, #"P7 [mbar]"=number, elapsed=number]}
}),
//remove uneeded column and expand the others
#"Removed Columns" = Table.RemoveColumns(#"Grouped Rows",{"month Year"}),
#"Expanded Elapsed Minutes" = Table.ExpandTableColumn(#"Removed Columns", "Elapsed Minutes", {"Date/Time", "P7 [mbar]"}, {"Date/Time", "P7 [mbar]"})
in
#"Expanded Elapsed Minutes"
Results from your shared workbook data
In Office/Excel 365
Filter Column (eg for January 2020)
E4: 1/1/2020
E5: 1/1/2020
Results
F4 (date/time 5th minute): =IF(COUNTIFS(Table1[Date/Time],">="&E4,Table1[Date/Time],"<" & EDATE(E4,1))=0,"",
LET(x,FILTER(Table1[Date/Time],(Table1[Date/Time]>=E4)*(Table1[Date/Time]<EDATE(E4,1))),
y, (x-INDEX(x,2))*1440,
z, XMATCH(5,y,1),
INDEX(x,z,1)))
G4: (Pressure 5th minute): =IF(F4="","",
LET(x,FILTER(Table1,(Table1[Date/Time]>=E4)*(Table1[Date/Time]<EDATE(E4,1))),
y, (INDEX(x,0,1)-INDEX(x,2,1))*1440,
z, XMATCH(5,y,1),
INDEX(x,z,2)))
F5: (Date next to last): =IF(COUNTIFS(Table1[Date/Time],">="&E5,Table1[Date/Time],"<" & EDATE(E5,1))=0,"",
LET(x,FILTER(Table1[Date/Time],(Table1[Date/Time]>=E5)*(Table1[Date/Time]<EDATE(E5,1))),
INDEX(x,COUNT(x)-1)))
G5: (Pressure next to last):=IF(F5="","",
LET(x,FILTER(Table1,(Table1[Date/Time]>=E5)*(Table1[Date/Time]<EDATE(E5,1))),
INDEX(x,COUNT(INDEX(x,0,1))-1,2)))

How to get missing previous quarter data

I'm having two different tables Finance and Budgets. There is a relationship between two tables.
Finance Table:
As of Date
Property Id
YTD Revenue
Quarter
3/31/21
1
$5,000
1
6/30/21
1
$6,000
2
3/31/21
2
$7,000
1
6/30/21
2
$8,000
2
Budgets:
As of Date
Property Id
Budget Revenue
Quarter
3/31/21
1
$10,000
1
6/30/21
1
$10,000
2
3/31/21
2
$11,000
1
The business doesn't want to enter the data if the Budget Revenue is same as the last quarter.
There is a quarter slicer on the page and I'm using Finance[Quarter]. Let's say I'm selecting 2nd quarter and there is no quarter 2 data for the property id 2 on the Budgets table and in this case we have to show Budget Revenue from last quarter i.e 3/31/2021($11,000).
Create a new Budget table.
Combine the two tables using JoinKind.FullOuter with all the columns except Revenue as the key
Expand the Budget Revenue column of the resultant table
Fill Down the Budget Revenue column
delete the unneeded columns and re-order the columns
let
Source = Table.NestedJoin(
Revenue, {"As of Date", "Property Id", "Quarter"},
Budget, {"As of Date", "Property Id", "Quarter"}, "Budget",
JoinKind.FullOuter),
#"Expanded Budget" = Table.ExpandTableColumn(Source, "Budget", {"Budget Revenue"}, {"Budget Revenue"}),
#"Filled Down" = Table.FillDown(#"Expanded Budget",{"Budget Revenue"}),
#"Removed Columns" = Table.RemoveColumns(#"Filled Down",{"YTD Revenue"}),
#"Reordered Columns" = Table.ReorderColumns(#"Removed Columns",{"As of Date", "Budget Revenue", "Quarter"})
in
#"Reordered Columns"

How can I calculate something which has other rows as inputs in power query?

I have a table in Power query, which besides other fields has the following key fields:
SKU | Year | Week | Customer | Transaction | Type | Value
As an example, some rows would be:
AB587 | 2019 | 12 | Tom | Purchase | Forecast |200
AB587 | 2019 | 12 | Tom | Sale | Forecast |15
AB587 |2019 | 11 | Tom | Stock | Actual |1455
This is a table with about 300,000 rows with all the SKUs and a couple of year's worth of transactions for all customers, and this gets into a very very useful pivot table that is used extensively. I now need to add something to the data to make the table even more useful.
I have the forecast for purchases and sales for the whole year along with the actuals of course and they follow the above pattern. I also have the stock for all the weeks but only the one in the past i.e. actuals only. I don't have the stock forecast, which is what I want to add. The calculation is as simple as:
Stock from previous week + Purchase forecast from this week - Sale forecast from this week
The end result which I am expecting is that there will now be more rows added which will have as an example:
AB587 |2019 | 12 | Tom | Stock | Forecast |1640
(I am using numbers from above to calculate)
This will now enable me not only to pivot Purchase and Sales but also stock levels which will be game changing.
I would love for anyone to help me with this in Power Query (I have tried a number of methods over weeks but have not cracked it)
To try and solve it myself:
I appended more rows essentially appending Week-1 data for all actual weeks from my source reducing potentially some calculation time. Then I pivoted my "Transaction column" leading to new columns i.e. Purchase, Sale, Stock and Stock-1, which made the Stock forecast calculation easy (that's what it appears to be).
The thing which I did not think about is: this is only good to calculate the first week stock forecast, but then there is no way that I know to use that just calculated stock forecast to calculate the next week's stock forecast.
Basically there is no way to save that stock forecast that I just calculated to be used for the next week's calculation.
I'm not clear on what you are asking when you say you say you want to use the "calculated stock forecast to calculate the next week's stock forecast". If you just want to generate the formula and result you gave as an example as a component of your dataset though, that is pretty simple.
Starting from this as a sample table of your data loaded into PQ that I'm calling "Data Table":
I create two reference queries based off it called StockForecast and CombinedDataTable
In the "StockForecast" query we will add three custom columns. Two are the CalcYear and CalcWeek columns that take "Stock Actual" records and increase the week by one. The third is a CalcValue column that takes "Sale Forecast" records and makes the value in those negative. The code in the editor looks like this:
Source = DataTable,
#"Added Custom" = Table.AddColumn(Source, "CalcYear", each
if [Transaction] = "Stock" and [Type] = "Actual" then
(if [Week] = 52 then [Year] + 1 else [Year])
else [Year]),
#"Added Custom1" = Table.AddColumn(#"Added Custom", "CalcWeek", each
if [Transaction] = "Stock" and [Type] = "Actual" then
(if [Week] = 52 then 1 else [Week] + 1)
else [Week]
),
#"Added Custom2" = Table.AddColumn(#"Added Custom1", "CalcValue", each
if [Transaction] = "Sale" and [Type] = "Forecast" then
[Value] * -1
else [Value]
),
Then you use the Group function and aggregate by Stock, Customer, CalcYear and CalcWeek, with a Sum on the CalcValue function. This gets the Stock Forecast value you are looking for. After that it's just a matter of adding a couple columns for identification and some cleanup.
#"Grouped Rows" = Table.Group(#"Added Custom2", {"Stock", "Customer", "CalcYear", "CalcWeek"}, {{"Value", each List.Sum([CalcValue]), type number}}),
#"Added Custom3" = Table.AddColumn(#"Grouped Rows", "Transaction", each "Stock"),
#"Added Custom4" = Table.AddColumn(#"Added Custom3", "Type", each "Forecast"),
#"Renamed Columns" = Table.RenameColumns(#"Added Custom4",{{"CalcYear", "Year"}, {"CalcWeek", "Week"}})
in
#"Renamed Columns"
Then end result of the data looks like this:
Then just go to the CombinedDataTable query, append the StockForecast query, and you have Stock Forecast values in your dataset.

Charting average sales per weekday on data composed of hours

I'm using PowerBI desktop and I'm creating a chart to display average sales per weekday:
My data is in the format below:
(sampled in Excel to remove sensitive information, added colors to facilitate visualization)
My problem is: since each day is broken in 24 rows (hours), my average is wrong by a factor of 24.
For example, if I select January-2019 in the slicer, which has five Tuesdays (weekday code: 2), I want to see on the bar number 2:
(sum of amount where weekday = 2) / 5
Instead, I'm calculating:
(sum of amount where weekday = 2) / (24 * 5)
I can think of some ways to get this right, but they involve custom columns or auxiliary tables. I'm sure there is a simpler answer using DAX and measures, but I'm still learning it.
How can I correctly calculate this?
Let's assume your table name is "Data". Create 3 DAX measures (not calculated columns):
Measure 1:
Total Amount = SUM(Data[Amount])
Measure 2:
Number of Days = DISTINCTCOUNT(Data[Date])
Measure 3:
Average Amount per Day = DIVIDE( [Total Amount], [Number of Days])
Drop the last measure into a chart, it should give you the expected result.
As I understand from your excel you are working with 3 different columns. You can better combine this to a datetime and let power-bi handle it.
Below m-language will do this for you:
let
Source = Excel.Workbook(File.Contents("C:\....\Test.xlsx"), null, true),
Sheet1_Sheet = Source{[Item="Sheet1",Kind="Sheet"]}[Data],
#"Promoted Headers" = Table.PromoteHeaders(Sheet1_Sheet, [PromoteAllScalars=true]),
#"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers",{{"date", type datetime}, {"hour", type time}, {"amount", type number}}),
#"Added Custom" = Table.AddColumn(#"Changed Type", "Date", each [date]+ Duration.FromText(Time.ToText([hour]))),
#"Removed Other Columns" = Table.SelectColumns(#"Added Custom",{"amount", "Date"}),
#"Filtered Rows" = Table.SelectRows(#"Removed Other Columns", each ([amount] <> 0))
in
#"Filtered Rows"
The trick is in the added column: #"Added Custom" = Table.AddColumn(#"Changed Type", "Date", each [date]+ Duration.FromText(Time.ToText([hour])))
Here I add the time to the date.
I also removed the empty (zero amount) rows, you do not need them.
I added the Date & weekday to the Axis so a user can now drill down from year, month, day to weekday.
Be aware you need to do the SUM of the amount, not the average.

Resources