DAX: How to construct a "piecewise" measure? - powerbi

I have a time series that looks very different before and after a certain date.
I have one measure that calculates the average value of the time series BEFORE that date, and another measure for the average value AFTER that date.
How do I melge them into 1 measure so that when I make a line chart in my front end tool, I see the one average before, and the other average after?

Got it! Trick was using HASONEVALUE and VALUES in tandem to get "data type" agreement. The "combined" measure now displays the piecewise average I was after.
EventCount:= DISTINCTCOUNT([Event_Id])
Avg Daily Event Count Pre:= calculate([EventCount] / DISTINCTCOUNT([EventDate]), Event[EventDate] <= date(2018, 3, 1))
Avg Daily Event Count Post:= calculate([EventCount] / DISTINCTCOUNT([EventDate]), Event[EventDate] >= date(2018, 4, 1))
Avg Daily Event Count Combined:= if (
HASONEVALUE(Event[EventDate]),
if(
values(Event[EventDate]) <= date(2018, 3, 1),
[Avg Daily Event Count Pre],
if(
values(Event[EventDate]) >= date(2018, 4, 1),
[Avg Daily Event Count Post],
blank()
)
)
)

Related

Cumulative total percentage in Power BI

I have a summary table in Power BI which shows how many days it takes for leads to convert to a sale. It has 2 columns, sum_convert (the amount of days in between lead creation date and converted date) and count_lead (the count of leads that have taken that amount of days to convert), both are numeric values. Here is an example of the data:
What I want, is a column next to count_lead that shows the running percentage total in the specific ascending order of sum_convert. Currently I've created a measure called lead_count which is the sum of count_lead. Then I've attempted to create the cumulative total with the following measure:
Cum_Lead = calculate([lead_count], FILTER(ALL(Convert_Count_Summary[Sum_Convert]), SUM(Convert_Count_Summary[count_lead]) <= [lead_count]))
This creates a cumulative total, but not in the specific sum_convert order, it's in the order of largest volume for count_lead. Any idea what I need to change so that it's in the order of sum_convert?
You could do this in Power Query using M:
= Table.AddColumn(#"Previous Step", "Cumulative_Count_PQ", each List.Sum(List.FirstN(#"Previous Step"[count_lead],_[sum_convert]+1)), type number)
Or as a calculated column using DAX:
Cumulative Count DAX =
CALCULATE (
SUM ( Convert_Count_Summary[count_lead] ),
ALL ( Convert_Count_Summary ),
Convert_Count_Summary[sum_convert] <= EARLIER ( Convert_Count_Summary[sum_convert] )
)
Edit:
Cumulative percentages in Power Query:
= Table.AddColumn(#"Previous Step", "Cumulative_Count_Percent_PQ", each List.Sum(List.FirstN(#"Previous Step"[count_lead],_[sum_convert]+1)) / List.Sum(#"Previous Step"[count_lead]), Percentage.Type)
Cumulative percentages calculated column in DAX:
Cumulative Count % DAX =
VAR _Numerator =
CALCULATE (
SUM ( Convert_Count_Summary[count_lead] ),
ALL ( Convert_Count_Summary ),
Convert_Count_Summary[sum_convert] <= EARLIER ( Convert_Count_Summary[sum_convert] )
)
VAR _Divisor =
SUM ( Convert_Count_Summary[count_lead] )
RETURN
DIVIDE (
_Numerator,
_Divisor
)

Conditional formatting based on condition on a measure

I have Data like this and I want to create a matrix visual. Since I need the Amount and Percentage in one column inside the matrix, I have created the following structured table in Power BI:
Description
Value
Sales Amount
50000
Sales Percentage
12%
Sales Amount
25000
Sales Percentage
25%
Sales Amount
75000
Sales Percentage
64%
Since it's not possible to store different format types in a single column in Power BI the percentage is stored as decimals and I created a measure to change the format based on the description column with the following code:
Value_formated =
VAR Val = SUM ( Table[Value] )
RETURN
SWITCH (
SELECTEDVALUE ( 'Table'[Description] ),
"Sales Amount", FORMAT ( Val, "0" ),
"Sales Percentage", FORMAT ( Val, "0.00%" ))
My question is how am I able to create a conditional formating to change the underlying color based on the percentage Value? Like for example if the percentage is negative, the percentage field should be red and if positive green. But since percentage is mixed with total number, how can I only filter the percentages? I have tried the following guide: https://xyloslearning.com/en/power-bi-using-a-measure-to-set-up-conditional-formatting/ but I couldn't select the coloring measure maybe because there are two different formats? Can anybody help me?
Thanks!
Create a format measure as follows:
Format =
VAR v = MIN ( 'Table'[Value] )
VAR d = MIN ( 'Table'[Description] )
RETURN
SWITCH(TRUE(),
d = "Sales Percentage" && v < 0, 0,
d = "Sales Percentage" && v >= 0, 1,
2)
Apply conditional formatting as follows:

DAX too slow - scalar value materialization problem

I have the following measure which calculates a node size for a graph chart table:
MEASURE tMeasures[m_ONA_nodeSize_flex] =
IF(
ISBLANK([m_ONA_rankFilter_nodes]),
BLANK(),
var empCnt = ROW(
"data",
CALCULATE(
SUMX( DISTINCT(tONAAppend[id]), 1),
FILTER(
ALL(tONAAppend),
NOT( ISBLANK([m_ONA_rankFilter_nodes]))
)
)
)
RETURN
IF(
empCnt > 25, ROUND( 1500 / empCnt, 0),
60
)
)
[m_ONA_rankFilter_nodes] is used to filter those nodes which exist in edges in the same table. These edges are also filtered using multiple conditions with a measure m_ONA_edgeValue_afterRank, which returns BLANK() if a row doesn't match filters and edge_value if it does.
The script before using tMeasures[m_ONA_nodeSize_flex] with included measures [m_ONA_rankFilter_nodes] and m_ONA_edgeValue_afterRank works relatively fast:
EVALUATE
TOPN(
501,
SUMMARIZECOLUMNS(
'tONAAppend'[tableName],
'tONAAppend'[name],
'tONAAppend'[nameFrom],
'tONAAppend'[nameTo],
'tONAAppend'[id],
'tONAAppend'[idFrom],
'tONAAppend'[idTo],
'tONAAppend'[photoSrc],
__DS0FilterTable,
__DS0FilterTable2,
__DS0FilterTable3,
__DS0FilterTable4,
"m_ONA_edgeValue_afterRank", 'tMeasures'[m_ONA_edgeValue_afterRank],
"m_ONA_rankFilter_nodes", 'tMeasures'[m_ONA_rankFilter_nodes]
),
'tONAAppend'[tableName],
1,
'tONAAppend'[name],
1,
'tONAAppend'[nameFrom],
1,
'tONAAppend'[nameTo],
1,
'tONAAppend'[id],
1,
'tONAAppend'[idFrom],
1,
'tONAAppend'[idTo],
1,
'tONAAppend'[photoSrc],
1
)
However, when I replace 'tMeasures'[m_ONA_rankFilter_nodes] with a 'tMeasures'[m_ONA_nodeSize_flex] it starts working dramatically slower:
EVALUATE
TOPN(
501,
SUMMARIZECOLUMNS(
'tONAAppend'[tableName],
'tONAAppend'[name],
'tONAAppend'[nameFrom],
'tONAAppend'[nameTo],
'tONAAppend'[id],
'tONAAppend'[idFrom],
'tONAAppend'[idTo],
'tONAAppend'[photoSrc],
__DS0FilterTable,
__DS0FilterTable2,
__DS0FilterTable3,
__DS0FilterTable4,
"m_ONA_edgeValue_afterRank", 'tMeasures'[m_ONA_edgeValue_afterRank],
"m_ONA_nodeSize_flex", 'tMeasures'[m_ONA_nodeSize_flex]
),
'tONAAppend'[tableName],
1,
'tONAAppend'[name],
1,
'tONAAppend'[nameFrom],
1,
'tONAAppend'[nameTo],
1,
'tONAAppend'[id],
1,
'tONAAppend'[idFrom],
1,
'tONAAppend'[idTo],
1,
'tONAAppend'[photoSrc],
1
)
As I understand, the problem is in DAX engine working: it tries to calculate the value of this measure for each row. I think the fastest algo could be calculate once, then send to storage, and then populate all rows with it.
How can I optimize and force DAX to work more efficient?
I found good articles which relate to this topic and then implemented approach with variables written there:
https://www.sqlbi.com/articles/optimizing-conditions-involving-blank-values-in-dax/
https://www.sqlbi.com/articles/understanding-eager-vs-strict-evaluation-in-dax/
It worked as expected - all measures put in special variables were materialized in a Storage Engine. The performance has increased dramatically.

Power BI M or DAX - Combine results of subquery (or measure) in aggregate with each row of a table, then roll up by date

I am looking for a way to roll up aggregates over time of a subquery and a time series in either DAX or M. The solutions I can think of are a measure applied per row in an aggregate in DAX, or join on a subquery expression in M, but I cannot find the functions to do this in the documentation.
An example SQL Solution:
SELECT
t1.Date,
t1.Month,
DailyOccupancy - Capacity 'Availability',
r.Rate,
(DailyOccupancy - Capacity) * r.Rate 'OverUnderCost'
FROM
Rate r
INNER JOIN
(SELECT
d.Date,
d.Month,
SUM(c.Capacity) 'DailyCapacity',
SUM(o.Occupancy) 'DailyOccupancy'
FROM
DimDate d
INNER JOIN Capacity c ON d.Date > c.StartDate AND d.Date < c.EndDate
INNER JOIN Occupancy o ON d.Date = o.Date
GROUP BY
d.Date,
d.Month,
r.Rate
) t1 ON o.Date = t1.Date
DAX Measures (works at the individual time step, but when using Capacity measure and a month worth of Occupancy, I get a single Capacity value and 30 Occupancy values summed)
DailyCapacity =
VAR currentDate = MAX ( DimDate[Date] )
RETURN
CALCULATE ( SUM('Capacity'[Capacity]),
FILTER ( 'Capacity',
( 'Capacity'[StartDate] <= currentDate
&& 'Capacity'[EndDate] >= currentDate )))
Available = CALCULATE(SUM('Occupancy'[Occupancy]) - ('Capacity'[DailyCapacity ]))
(Works for each individual time step, doesn't roll up over time)
Is there a DAX method to sum the Capacity measure minus the Available aggregate for each timestep?
Another approach is to use M to create a "DailyCapacity" table. In SQL:
SELECT
d.Date,
SUM(c.Capacity) 'DailyCapacity',
FROM
DimDate d
INNER JOIN Capacity c ON d.Date > c.StartDate AND d.Date < c.EndDate
GROUP BY
d.Date
but I can't find a way to join on a boolean expression in M, only keys. Is there a way to join on a boolean expression, not keys in M?
My tables:
DimDate (Date, day, Month, Year, Billing Month, FY)
----------
1/1/2019, 1, 1, 2019, 1, 2019
1/2/2019, 2, 1, 2019, 1, 2019
... Every time step possible
Capacity (StartDate, EndDate, Capacity, Notes)
----------
1/1/2019, 12/31/2019, 40, "Annual Cap"
6/1/2019, 9/15/2019, 30, "Summer Peak"
Occupancy (Date, Occupancy, Location)
----------
1/1/2019, 20, 1
1/1/2019, 17, 2
1/2/2019, 30, 1
1/2/2019, 9, 2
1/3/2019, 22, 1
1/3/2019, 20, 2
Rate (Date, Rate)
----------
1/1/2019, $49.99
1/2/2019, $64.99
... etc.
Needed Output
Available Space:
----------
1/1/2019, 3
1/2/2019, 1
1/3/2019, -2
1/4/2019, 0
And aggregates on rolled up time steps:
SUM(Available Space * Current Day's Rate) per month, quarter, year, etc. Including over and under capacity totals as negative and positive.
To do this in DAX, let's write a DailyCapacity measure (very similar to yours) that we'll use in another measure. (I use SELECTEDVALUE here so it's more obvious if I'm dealing with more than one value but your MAX and SUM should work too.)
DailyCapacity =
VAR currentDate = SELECTEDVALUE ( DimDate[Date] )
RETURN
CALCULATE (
SELECTEDVALUE ( Capacity[Capacity] ),
FILTER (
Capacity,
Capacity[StartDate] <= currentDate &&
Capacity[EndDate] >= currentDate
)
)
Now to calculated available space, we'll iterate through each row of Occupancy and call the measure we just defined (assuming your DimDate table has a relationship that filters Occupancy).
AvailableSpace = SUMX ( Occupancy, [DailyCapacity] - Occupancy[Occupancy] )
This measure should roll up as expected since it's computing the daily capacity for each row, not just once.
Similarly, if you write a measure for DailyRate like you did for DailyCapacity, then you can write an opportunity cost measure SUMX ( DimDate, [AvailableSpace] * [DailyRate] ).

Power BI - Line Chart with changing color for trend

Just wondering if it's possible to create a chart like below.
I have created the following measures in DAX:
1. [Total Sales]
2. [PM Sale] =
CALCULATE([TotalSales], PARALLELPERIOD('Date'[Date], -1, MONTH)) // Previous month Sales
3. [Indicator] = IF([TotalSales] - [PM Sale] >=0, 1, 0)
4. [IndicatorText] = IF([TotalSales] - [PM Sale] >=0, "UP", "DOWN")
I thought adding the [Indicator] or [IndicatorText] to "Legend" property of line chart would be possible and then be able to change the color, but it is not.
Any ideas?
Thanks,
Shiv
This isn't exactly what you are requesting, but a waterfall chart works pretty well here.
Create a measure to be the difference from the last month and use that for the waterfall chart's y-axis with the date in the category section.
Diff = [Total Sales] - CALCULATE([Total Sales], PARALLELPERIOD('Date'[Date], -1, MONTH))
You can also use a regular bar chart with two series:
Up = IF([Diff] >= 0, [Total Sales], BLANK())
Down = IF([Diff] < 0, [Total Sales], BLANK())
If you convert this to a line chart, it would look like this (you need to set the x-axis to categorical instead of continuous):
It's possible to tweak the measures a bit by looking at the next month in addition to the previous month and get what you want for this particular case, but it won't work correctly in general:
You can't tell from the image, but the first red line segment is covering a green line segment. If red and green segments alternate, then this methodology breaks down.
Here are the adjusted measured for the chart above:
Forward = IF(ISBLANK(PARALLELPERIOD('Date'[Date] , 1, MONTH)),
BLANK(),
CALCULATE([Total Sales]),
PARALLELPERIOD('Date'[Date], 1, MONTH))
- [Total Sales])
Up = IF([Diff] >= 0 || [Forward] >= 0, [Total Sales], BLANK())
Down = IF([Diff] < 0 || [Forward] < 0, [Total Sales], BLANK())