Overlapping periods value summary on chart in Power BI - powerbi

I don't know what I'm looking for maybe that is why I can't find solution online.
Just trying to show a data on simple chart with time axis.
Example of data I have:
ID
StartTime
EndTime
Quantity
1
11:00:00
12:00:00
10
2
11:30:00
12:20:00
10
3
12:00:00
13:00:00
10
4
13:40:00
14:00:00
10
5
13:50:00
15:00:00
10
Now on chart I would like to show:
Axis
Value on Chart
11:00:00
10
11:15:00
10
11:30:00
20
11:45:00
20
12:00:00
20
12:15:00
20
12:30:00
10
12:45:00
10
...
...
So overlapping period values should show on chart as sum.
Axis interval 15 min is just for example, but if required it could be fixed to 10 or 15 minutes.

In PQ M code you could:
Create a list of ALL the 15 minute bins from the earliest Start to the Latest end
Create a list of the 15 minute bins for each row Start..End
Join the two with JoinType.FullOuter
Group by the bin, return the Sum for each interval and Sort
Source
M Code
let
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMlTSUTI0tDIwACIQ0wjBNFCK1YlWMoIoMEYoMEJVYIymzRjdBBOIqAlM1ARdgSlEgSlM1BRFQSwA", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [ID = _t, StartTime = _t, EndTime = _t, Quantity = _t]),
//set data types
#"Changed Type" = Table.TransformColumnTypes(Source,{
{"ID", Int64.Type}, {"StartTime", type time}, {"EndTime", type time}, {"Quantity", Int64.Type}}),
//create List of all 15 minute intervals from earliest Start time to latest End time
quarterHours = List.Times(
List.Min(#"Changed Type"[StartTime]),
Duration.TotalMinutes(
List.Max(#"Changed Type"[EndTime]) - List.Min(#"Changed Type"[StartTime]))/15,#duration(0,0,15,0)),
//compute a List of 15 minute bins for each row
// where Start Time is rounded down to the 15 minute interval; and End Time is rounded up to the 15 minute interval
#"Added Custom" = Table.AddColumn(#"Changed Type", "Time Bin",
each List.Transform(
{Number.RoundDown(Number.From([StartTime])*96,0)..Number.RoundUp(Number.From([EndTime])*96,0)-1},
each _/96)),
//expand the Time Bin list to one entry per 15 minute interval
//and set the data type
#"Expanded Custom" = Table.ExpandListColumn(#"Added Custom", "Time Bin"),
#"Changed Type1" = Table.TransformColumnTypes(#"Expanded Custom",{{"Time Bin", type time}}),
//Join the column that has ALL of the 15 minute bins (so as to account for those intervals with no entries
addAllTimes = Table.Join(#"Changed Type1","Time Bin",
Table.FromColumns({quarterHours},type table[Bin=Time.Type]),"Bin",JoinKind.FullOuter),
//Group by Bin and Sort by time
#"Grouped Rows" = Table.Group(addAllTimes, {"Bin"}, {{"Quantity", each List.Sum([Quantity]), type nullable number}}),
#"Sorted Rows" = Table.Sort(#"Grouped Rows",{{"Bin", Order.Ascending}})
in
#"Sorted Rows"
Results

Define a new table corresponding to the Axis column you show and then write a measure to use for the values along these lines:
ChartValue =
VAR CurrTime = SELECTEDVALUE ( NewTable[Axis] )
RETURN
CALCULATE (
SUM ( Data[Amount] ),
Data[StartTime] <= CurrTime,
Data[EndTime] >= CurrTime
)

In my example I put the data from your question into a table (insert > table) and named it tblData.
The axis-data are in table tblAxis, Quantity is calculated using the the SUMIFS-Formula which checks for Start and End-Time.
=SUMIFS(tblData[Quantity];tblData[StartTime];"<=" & [#Axis];tblData[EndTime];">" & [#Axis])

Thank you both.
Both solutions works!!
However for my limited knowledge, makes it easier for me to use ike solutions.

Related

Create an average table excluding some values

I have a table with this structure:
Date
DeviceID
Value
01/01/2022
SensorA
1200
01/01/2022
SensorB
1300
01/01/2022
SensorC
900
02/01/2022
SensorA
500
02/01/2022
SensorB
50
02/01/2022
SensorC
39
I'm interesting in create a new table that have the average by day of all sensors values but excluding the values which are lower than the average of all sensors by day.
For example for 01/01/2022 the average of all sensors values is:
(1200+1300+900) / 3 = 1133.3
We then calculate the average sensor value for the day, excluding SensorC because its value is below 1133.3:
(1200+1300) / 2 = 1250
The final table have to looks like this:
Date
Value
01/01/2022
1250
02/01/2022
500
Is it possible? Hope you can help me
Thanks in advance!
This does exactly what you are asking for:
Avg Excl =
VAR _avg = AVERAGE ( 'Table'[Value] )
RETURN
CALCULATE (
AVERAGE ( 'Table'[Value] ) ,
'Table'[Value] > _avg
)
Result:
If you want to persist a DAX table to your model, you just use this code together with SUMMARIZE:
New_Table =
SUMMARIZE (
'Table' ,
'Table'[Date] ,
"Value",
VAR _avg = AVERAGE ( 'Table'[Value] )
RETURN
CALCULATE (
AVERAGE ( 'Table'[Value] ) ,
'Table'[Value] > _avg
)
)
Result:
For good measure, here is some Power Query code to achieve the same result. Usually this is best practice due to compression efficiency and such in Power BI. As a bonus, it also makes working with the pbix file smoother since you don't have to wait for the table to recalculate all the time when you add/remove/edit DAX components.
This solution is your best bet if this his how you actually want your table to look, since you can get rid of the original table in the model altogether and not have it in the model as a dependency. You can paste this into a blank query:
let
Source = Table.FromRecords({
[Date="2022-01-01", DeviceID="SensorA",Value=1200],
[Date="2022-01-01", DeviceID="SensorB",Value=1300],
[Date="2022-01-01", DeviceID="SensorC",Value=900],
[Date="2022-01-02", DeviceID="SensorA",Value=500],
[Date="2022-01-02", DeviceID="SensorB",Value=50],
[Date="2022-01-02", DeviceID="SensorC",Value=39]
}),
#"Changed Type" = Table.TransformColumnTypes(Source,{
{"Date", type date},
{"DeviceID", type text},
{"Value", Int64.Type}
}),
#"Grouped Rows" = Table.Group(#"Changed Type", {"Date"}, {
{"Avg", each List.Average([Value]), type nullable number},
{"Data", each _, type table [Date=nullable date, DeviceID=nullable text, Value=nullable number]}
}),
#"Expanded Data" = Table.ExpandTableColumn(#"Grouped Rows", "Data", {"Value"}, {"Data.Value"}),
#"Added Custom" = Table.AddColumn(#"Expanded Data", "Custom", each if [Data.Value] > [Avg] then [Data.Value] else null),
#"Grouped Rows1" = Table.Group(#"Added Custom", {"Date"}, {{"Avg Value", each List.Average([Custom]), type nullable number}})
in
#"Grouped Rows1"
Since you are using the Device ID column in the table, it shows all of them separately. Can you remove the DeviceID column and try the value column as average?

How to convert decimal date type column to time data type in power query/power BI?

The column consist of minutes that is in the decimal format. This is to be converted to Time format.
Example: The 5th record is 61 minutes and 6 seconds.
This is to be displayed as 1 hour, 1 minute and 6 seconds - (01:01:06).
How to solve this problem in power query editor/ power BI?
Plenty of options. You can do it in Power Query, in DAX as a measure or as a calculated column.
Here is a simple calculated column:
Formatted =
VAR _hrs = QUOTIENT ( [minutes_watched] , 60 )
VAR _mins = INT ( [minutes_watched] - _hrs * 60 )
VAR _sec = MOD ( [minutes_watched] , 1.0 ) * 60
RETURN
FORMAT(_hrs,"00")&":"&FORMAT(_mins,"00")&":"&FORMAT(_sec,"00")
This also handles weirdos watching for more than 24 hrs, at which a TIME data type would overflow:
It is also easily written as a measure where the minutes watched can be an aggregation instead:
Formatted Minutes :=
VAR _agg_time = [Sum minutes watched] // Aggregation measure of choice goes here
VAR _hrs = QUOTIENT ( _agg_time , 60 )
VAR _mins = INT ( _agg_time - _hrs * 60 )
VAR _sec = MOD ( _agg_time , 1.0 ) * 60
RETURN
FORMAT(_hrs,"00")&":"&FORMAT(_mins,"00")&":"&FORMAT(_sec,"00")
Which gives you this result:
Simply divide your minutes decimal by (24 * 60) and you can convert it into Time format:
let
Source = Table.FromList({"0.3", "2.7", "46.8", "61.6"}),
#"Renamed Columns" = Table.RenameColumns(
Source,{{"Column1", "minutes_watched"}}),
#"Changed Type" = Table.TransformColumnTypes(
#"Renamed Columns",{{"minutes_watched", type number}}),
#"Added Custom" = Table.AddColumn(
#"Changed Type", "Custom", each [minutes_watched] / 60 / 24),
#"Changed Type1" = Table.TransformColumnTypes(
#"Added Custom",{{"Custom", type time}})
in
#"Changed Type1"
Add column ... custom column.. with formula
= #duration(0,0,Number.From(Text.Split(Text.From([minutes_watched]),"."){0}),Number.From(Text.Split(Text.From([minutes_watched]),"."){1}))

previous pay period using dax

I am very new to power bi and power query and while I have searched for a solution, I may be so new that I am unaware that a similar solution is here and could solve my issue but here goes.
I have been tasked with taking a table of data (which I don't have access to alter) and display in Power BI current pay period data along side data from the previous pay period (essentially doing an Excel vlookup for the previous period (each pay period is 14 days apart)) and then creating calculations to show the change in position vacancy from one pay period to the next. I only have Power Query and Power BI in which to make this happen. I created a key from different columns and then a linked a referenced table to pull the previous dates info but this didn't work once I started putting slicers on the matrix. Right now I'm trying to assign each pay period a identifier which grows by one with each later date (completed) and then create a column which reads the table and gives the previous pay period vacancy hours.
Empl Number
Sched Hours
Budg Hours
Vacant Hours
Pay Period
1
0.00
9.00
9.00
01/01/2022
2
22.5
22.5
0.00
01/01/2022
2
0.00
22.5
22.5
01/15/2022
3
0.00
37.5
37.5
01/01/2022
3
37.5
37.5
0.00
01/15/2022
3
37.5
37.5
0.00
01/29/2022
I am expecting to see something like the image below in Power BI:
Pay Period
Total Vacant Hours
Vacant Hour Change
01/01/2022
46.5
01/15/2022
60.0
13.5
01/29/2022
0.00
-60.0
02/12/2022
0.00
0.00
try to create a new table
Modelling --> New Table
previous pay period using dax = SUMMARIZE (
'Table',
'Table'[Pay Period],
"Sum Vacant", SUM ( 'Table'[Vacant Hours ] ),
"Balance Vacant",
SUM ( 'Table'[Vacant Hours ] )
- CALCULATE (
SUM ( 'Table'[Vacant Hours ] ),
FILTER (
ALL ( 'Table' ),
'Table'[Pay Period]
= CALCULATE (
MAX ( 'Table'[Pay Period] ),
FILTER (
ALL ( 'Table' ),
'Table'[Pay Period] < SELECTEDVALUE ( 'Table'[Pay Period] )
)
)
)
)
)
In Power Query: (Read the comments to understand the algorithm)
let
//Change next line to reflect your actual data source
Source = Excel.CurrentWorkbook(){[Name="Table8"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{
{"Empl Number", Int64.Type}, {"Sched Hours", type number}, {"Budg Hours", type number},
{"Vacant Hours", type number}, {"Pay Period", type date}}),
//Group by pay period and aggregate Sum of Vacant Hours
#"Grouped Rows" = Table.Group(#"Changed Type", {"Pay Period"}, {
{"Vacant Hours", each List.Sum([Vacant Hours]), type nullable number}}),
//Add a column which is a "shifted" vacant hours column
// This will put this and previous pay period on same row
#"Shifted Vacant Hours" = Table.FromColumns(
Table.ToColumns(#"Grouped Rows") & {{null} & List.RemoveLastN(#"Grouped Rows"[Vacant Hours],1)},
type table[Pay Period=date, Total Vacant Hours=number, Shifted Vacant=number]),
//add a column subtracting Shifted Vacant from Total Vacant
#"Added Custom" = Table.AddColumn(#"Shifted Vacant Hours", "Vacant Hour Change", each if [Shifted Vacant] = null then null else
[Total Vacant Hours]-[Shifted Vacant]),
//Remove the Shifted Vacant column
#"Removed Columns" = Table.RemoveColumns(#"Added Custom",{"Shifted Vacant"})
in
#"Removed Columns"
Edit for different grouping
If you want to have a table where the Vacant hour change is computed per employee (for each time period), then we would Group By: Empl Number, and write a custom aggregation to add the "Vacancy Change" column.
The code below assumes only a single entry per customer per pay period, as you show in your example. If there might be multiple entries per employee per pay period, minor code changes will be needed.
let
//Change next line to reflect your actual data source
Source = Excel.CurrentWorkbook(){[Name="Table8"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{
{"Empl Number", Int64.Type}, {"Sched Hours", type number}, {"Budg Hours", type number},
{"Vacant Hours", type number}, {"Pay Period", type date}}),
//Group by pay period and custom aggregaton to assess by Employee for each time period
//Assumes only a single entry per employee per time period
#"Grouped Rows" = Table.Group(#"Changed Type", {"Empl Number"}, {
{"Calcs", (t)=> let
subTable = Table.FromColumns(
Table.ToColumns(t) & {{null} & List.RemoveLastN(t[Vacant Hours])},
{"Empl Number", "Sched Hours", "Budg Hours", "Vacant Hours", "Pay Period", "Shifted Vacant"}),
#"Vacancy Change" = Table.AddColumn(subTable,"Vacant Hour Change", each [Vacant Hours] - [Shifted Vacant]),
#"Remove Shifted" = Table.RemoveColumns(#"Vacancy Change",{"Shifted Vacant"})
in #"Remove Shifted",
type table[Empl Number=Int64.Type, Sched Hours=number, Budg Hours=number, Vacant Hours=number,
Pay Period=date, Vacant Hour Change=number]
}}),
//expand the table; set columns in proper order; remove unneeded columns
#"Expanded Calcs" = Table.ExpandTableColumn(#"Grouped Rows", "Calcs", {"Sched Hours", "Budg Hours", "Vacant Hours", "Pay Period", "Vacant Hour Change"}),
#"Reordered Columns" = Table.ReorderColumns(#"Expanded Calcs",{"Empl Number", "Pay Period", "Sched Hours", "Budg Hours", "Vacant Hours", "Vacant Hour Change"}),
#"Removed Columns" = Table.RemoveColumns(#"Reordered Columns",{"Sched Hours", "Budg Hours"})
in
#"Removed Columns"

Repeat the last value over time

I have a table with power plant capacities in different years. There are only entries when something changes in the capacities. In the years not listed, the last value applies.
Plant
Year
Capacity
Cottam
2003
800
Cottam
2009
600
Cottam
2015
800
Drax
2000
600
Drax
2005
1200
Drax
2010
1800
Drax
2013
1200
Drax
2020
0
Ironbridge
2007
500
Ironbridge
2015
0
Now I would like to transform the initial table, so that I also have values for all years in between and can display them in a stacked column chart, for example. The result should look like shown in the table below. Marked in yellow are the numbers from the initial table.
You can do this easily in the Query Editor in M code.
To reproduce, paste the code below into a blank query:
let
//change next line to reflect your actual data source
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45Wcs4vKUnMVdJRMjIwMAZSFgYGSrE6qOKWQMoMU9zQFEm9S1FiBUS1AZJqhChIraERurAhSLEhhhmGxlhVG4FUQ8Q8i/LzkooyU9JTIcabAylTA6xyYGcCZWIB", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Plant = _t, Year = _t, Capacity = _t]),
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Plant", type text}, {"Year", Int64.Type}, {"Capacity", Int64.Type}}),
//generate Table of all years
#"All Years" = Table.FromColumns(
{List.Numbers(List.Min(#"Changed Type"[Year]), List.Max(#"Changed Type"[Year])- List.Min(#"Changed Type"[Year]) + 1 )}),
//Group by Plant
// Aggregate by joining with the All Years table and "Fill Down" to replace blanks with previous year.
// then expand the grouped column
#"Group by Plant" = Table.Group(#"Changed Type","Plant",{
{"Joined", each Table.FillDown(Table.Join(#"All Years","Column1",_,"Year",JoinKind.FullOuter),{"Capacity"})}
}),
#"Expanded Joined" = Table.ExpandTableColumn(#"Group by Plant", "Joined", {"Column1", "Capacity"}, {"Column1", "Capacity"}),
//Replace nulls with zero's
#"Replaced Value" = Table.ReplaceValue(#"Expanded Joined",null,0,Replacer.ReplaceValue,{"Capacity"}),
//Pivot on year
// then set the data types
#"Pivoted Column" = Table.Pivot(Table.TransformColumnTypes(#"Replaced Value", {{"Column1", type text}}, "en-US"),
List.Distinct(Table.TransformColumnTypes(#"Replaced Value", {{"Column1", type text}}, "en-US")[Column1]), "Column1", "Capacity"),
//set data type
#"Changed Type1" = Table.TransformColumnTypes(#"Pivoted Column",
List.Transform(List.Sort(List.RemoveFirstN(Table.ColumnNames(#"Pivoted Column"),1), Order.Ascending), each {_, Int64.Type}))
in
#"Changed Type1"
Edit Note:
Actually, to create the graph in Power BI, you do NOT want to pivot the data, so the shorter code:
let
//change next line to reflect your actual data source
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45Wcs4vKUnMVdJRMjIwMAZSFgYGSrE6qOKWQMoMU9zQFEm9S1FiBUS1AZJqhChIraERurAhSLEhhhmGxlhVG4FUQ8Q8i/LzkooyU9JTIcabAylTA6xyYGcCZWIB", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Plant = _t, Year = _t, Capacity = _t]),
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Plant", type text}, {"Year", Int64.Type}, {"Capacity", Int64.Type}}),
//generate Table of all years
#"All Years" = Table.FromColumns(
{List.Numbers(List.Min(#"Changed Type"[Year]), List.Max(#"Changed Type"[Year])- List.Min(#"Changed Type"[Year]) + 1 )}),
//Group by Plant
// Aggregate by joining with the All Years table and "Fill Down" to replace blanks with previous year.
// then expand the grouped column
#"Group by Plant" = Table.Group(#"Changed Type","Plant",{
{"Joined", each Table.FillDown(Table.Join(#"All Years","Column1",_,"Year",JoinKind.FullOuter),{"Capacity"})}
}),
#"Expanded Joined" = Table.ExpandTableColumn(#"Group by Plant", "Joined", {"Column1", "Capacity"}, {"Year", "Capacity"}),
//Replace nulls with zero's
#"Replaced Value" = Table.ReplaceValue(#"Expanded Joined",null,0,Replacer.ReplaceValue,{"Capacity"}),
#"Changed Type1" = Table.TransformColumnTypes(#"Replaced Value",{{"Year", Int64.Type}, {"Capacity", Int64.Type}})
in
#"Changed Type1"
Then, in Power BI, you can generate this:
Note:
The code below presents the Table FillDown / Table Join sequence from the first code using variables and more comments. Should be easier to understand (might be less efficient, though)
...
{"Joined", each
let
//join the subtable with the All Years table
#"Joined Table" = Table.Join(#"All Years", "Column1", _, "Year", JoinKind.FullOuter),
//Fill down the Capacity column so as to fill with the "last year" data
// since that column will contain a null after the Table.Join for years with no data
#"Fill Down" = Table.FillDown(#"Joined Table",{"Capacity"})
in
#"Fill Down"
}
...
Here's how to solve this (more easily) in DAX:
Prerequisite is separate Calendar table with a 1:many relation on the year
Calendar =
SELECTCOLUMNS(
GENERATESERIES(
MIN(Plants[Year]),
MAX(Plants[Year])
),
"Year", [Value]
)
Next calculate the Last Given Capacity per year
Last Given Capacity =
VAR current_year =
MAX(Calendar[Year])
VAR last_capacity_year =
CALCULATE(
MAX(Plants[Year]),
'Calendar'[Year] <= current_year
)
RETURN
CALCULATE(
MAX(Plants[Capacity]),
Calendar[Year] = last_capacity_year
)
Finally put it all together in a Stacked Column Chart with
X-axis: 'Calendar'[Year]
Y-axis: [Last Given Capacity]
Legend: 'Plants'[Plant]

Calculated columns bases on the past 3 months in Power query

First time trying to use M in power query... what I have is this table
I need to create two columns that per each row (combination of CD_Loja x CD_Produto )returns me the sum of QT_VENDA for that combination divided by the # of days in the past 3 months. The other column is pretty much the same but with the sum of  VL_VENDA_LIQ  Instead.
I.e: For the first row I want to sum up all QT_VENDA that matches CD_PRODUTO =1001930 AND CD_LOJA = 151 in the past 3 months (the DATE column has daily data) and divide it by the number of days in those 3 months.
Is there a way to do so ? And how do I go about this ?
Thanks in advance.
In powerquery, M, something along these lines
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Changed Type" = Table.Buffer(Table.TransformColumnTypes(Source,{{"DATA", type date}, {"CD_PRODUCTO", type text}, {"CD_LOIA", type text}, {"QT_VENDA", Int64.Type}, {"VL_VENDA_LIQ", Int64.Type}})),
#"Added Custom" = Table.AddColumn(#"Changed Type" ,"QT_VENDA_90",(i)=>List.Average(Table.SelectRows(#"Changed Type" , each [CD_PRODUCTO]=i[CD_PRODUCTO] and [CD_LOIA]=i[CD_LOIA] and [DATA] <= i[DATA] and [DATA] >= Date.AddDays(i[DATA] ,-90)) [QT_VENDA]), type number),
#"Added Custom2" = Table.AddColumn(#"Added Custom" ,"VL_VENDA_LIQ_90",(i)=>List.Average(Table.SelectRows(#"Changed Type" , each [CD_PRODUCTO]=i[CD_PRODUCTO] and [CD_LOIA]=i[CD_LOIA] and [DATA] <= i[DATA] and [DATA] >= Date.AddDays(i[DATA] ,-90)) [VL_VENDA_LIQ]), type number)
in #"Added Custom2"
You can try a Measure like below-
your_expected_value =
var current_row_data = MIN(your_table_name[DATA])
--I Guess the column name should be Date instead
var current_row_data_minus_90_day = MIN(your_table_name[DATA]) - 90
var current_row_cd_produto = MIN(your_table_name[CD_PRODUTO])
var current_row_cd_loja = MIN(your_table_name[CD_LOJA])
RETURN
CALCULATE(
SUM(your_table_name[QT_VENDA]),
FILTER(
ALL(your_table_name),
your_table_name[DATA] >= current_row_data_minus_90_day
&& your_table_name[DATA] <= current_row_data
&& your_table_name[CD_PRODUTO] = current_row_cd_produto
&& your_table_name[CD_LOJA] = current_row_cd_loja
)
)/90
--Here 90 used for static 3 month consideration