DAX - How to sum the most recent values by group? - powerbi

I have 2 tables, the first is a sanitised version of the data, the second is what I need to calculate.
AnimalCount
Animal DATE Count
Lion 2019-01-01 2
Cat 2019-01-01 45
Cat 2019-01-02 40
Fish 2019-01-02 900
Cat 2019-01-03 50
Fish 2019-01-05 800
Fish 2019-01-06 1200
Cat 2019-01-07 45
Lion 2019-01-07 1
Lion 2019-01-08 0
Calculated
DATE (unique) Sum of latest Count by Animal
2019-01-01 47 = 45 Cats + 2 Lions
2019-01-02 942 = 900 Fish + 40 Cats + 2 Lions
2019-01-03 952 = 50 Cats + 900 Fish + 2 Lions
2019-01-05 852 = 800 Fish + 50 Cats + 2 Lions
2019-01-06 1252 = 1200 Fish + 50 Cats + 2 Lions
2019-01-07 1246 = 1 Lion + 45 Cats + 1200 Fish
2019-01-08 1245 = 45 Cats + 1200 Fish
Here are the queries I've tried so far, both of them give slightly different results to what I'm looking for.
I'd be super grateful for any suggestions?
Query 1.
LatestTotal = SUMX(
GROUPBY(
FILTER(
AnimalCount,
[DATE] <= EARLIER([DATE])
),
[Animal],
"LatestGroupTotal",
SUMX(CURRENTGROUP(), [Count])
),
[LatestGroupTotal]
)
Query 2.
LatestTotal = SUMX(
ADDCOLUMNS(
ADDCOLUMNS(
VALUES(AnimalCount[Animal]),
"AnimalName",
[Animal],
"LastDate",
CALCULATE(
MAX(AnimalCount[DATE]),
NOT(ISBLANK(AnimalCount[Count]))
)
),
"LastValue",
SUMX(
FILTER(
AnimalCount,
[AnimalName] = [Animal]
&& [DATE] = [LastDate]
),
[Count]
)
),
[LastValue]
)

First, create a measure for total animal count (for further convenience):
Total Animal Count = SUM(AnimalCount[Count])
Then, add this measure:
Latest Total =
VAR
Current_Date = MAX ( AnimalCount[DATE] )
VAR
All_Latest_Dates =
ADDCOLUMNS (
ALL ( AnimalCount[Animal] ),
"Last Date", CALCULATE (
MAX ( AnimalCount[DATE] ),
AnimalCount[DATE] <= Current_Date )
)
VAR Animal_Latest_Dates =
TREATAS ( All_Latest_Dates, AnimalCount[Animal], AnimalCount[DATE] )
RETURN
CALCULATE ( [Total Animal Count], Animal_Latest_Dates )
Result:
Explanation:
This formula requires an understanding of "data lineage" concept in DAX:
Understanding Data Lineage in DAX
In short:
store date visible in a current context in a variable;
then, generate a virtual table ("All_Latest_Dates"), that consists of all animals, and their last known date for each "current" date;
next, apply this virtual table to the AnimalCount table using TREATAS (please read the article to understand how it works);
finally, calculate animal count using the virtual table as a new filter context.
This approach results in an optimal query execution plan, and the formula should give you good performance even if your data is large.

How about this?
LatestTotal =
VAR CurrDate =
MAX ( AnimalCount[DATE] )
VAR Summary =
SUMMARIZE (
FILTER ( ALLSELECTED ( AnimalCount ), AnimalCount[DATE] <= CurrDate ),
AnimalCount[Animal],
"LastDate", MAX ( AnimalCount[DATE] )
)
VAR CountAtDate =
ADDCOLUMNS (
Summary,
"Count", LOOKUPVALUE (
AnimalCount[Count],
AnimalCount[Animal], [Animal],
AnimalCount[DATE], [LastDate]
)
)
RETURN
SUMX ( CountAtDate, [Count] )
First, we summarize the table by Animal looking for the last date associated with each one that is less than or equal to the date of the row we're in (CurrDate).
Then we look up the count for that animal on that date and add all those counts together.

Related

How can i get the value of a column for the max avalaible date when the selected date in the filter is bigger than the max date avalaible

lets say i have a table1 with three products and the max date i have available data for each one
product
max_date_aval
milk
13/9/2022
paper
15/9/2022
oranges
15/9/2022
a table2 with the product name, the rec_date and the sales amount
rec_date
product
sales
12/9/2022
milk
13
12/9/2022
paper
10
12/9/2022
oranges
11
13/9/2022
milk
12
13/9/2022
paper
14
13/9/2022
oranges
16
14/9/2022
paper
17
14/9/2022
oranges
9
15/9/2022
paper
12
15/9/2022
oranges
11
and a typical calendar date that i use as a filet in my dashboard.
I have created a table that displays the three products and the sales like this ( when i choose "13/9/2022" in the date filter)
product
daily_sales
MTD
milk
12
300
paper
14
329
oranges
16
321
what i want to achieve is that, when i choose a date in the filter that its bigger than the max available date, i want the table to display the value for the maximum date i have availiable.
For example, if i choose "14/9/2022" in the filter, i want th e table to be like this
product
daily_sales
MTD
milk
13
300
paper
17
346
oranges
9
330
Currently what im getting is the one bellow ( which makes sense but i need to have all three products )
product
daily_sales
MTD
paper
17
346
oranges
9
330
In order to achieve this, the dates used within the slicer need to be unrelated to the other two tables.
You can do this by either manually removing/deactivating the relationship between the Table2 and Calendar tables and then replacing your current Sales measure with the following:
MyMeasure1 :=
VAR SelectedDate =
MIN( Celandar[Date] )
VAR MaxAvailableDate =
CALCULATE(
MAX( Table1[max_date_aval] ),
Table1[max_date_aval] <= SelectedDate
)
VAR DateToUse =
IF( ISBLANK( MaxAvailableDate ), SelectedDate, MaxAvailableDate )
RETURN
SUMX( Table2, IF( Table2[rec_date] = DateToUse, Table2[sales] ) )
or, if you prefer to leave that relationship active, it can be deactivated in-measure:
MyMeasure2 :=
VAR SelectedDate =
MIN( Celandar[Date] )
VAR MaxAvailableDate =
CALCULATE(
MAX( Table1[max_date_aval] ),
Table1[max_date_aval] <= SelectedDate
)
VAR DateToUse =
IF( ISBLANK( MaxAvailableDate ), SelectedDate, MaxAvailableDate )
RETURN
CALCULATE(
SUMX( Table2, IF( Table2[rec_date] = DateToUse, Table2[sales] ) ),
CROSSFILTER ( Celandar[Date], Table2[rec_date], NONE )
)

How to output the ID of the maximum average in Power BI?

I have a table with data. I want to find the average by Criteria 1 for each Department ID (Department IDs are repeated) and output the value of the Department ID that has the maximum average. How can I do this? [I attach a table with data.]
Department ID
Criteria 1
DEP 001
4
DEP 002
2
DEP 003
1
DEP 004
5
DEP 001
3
DEP 003
2
DEP 002
4
DEP 001
3
DEP 004
2
DEP 001
1
DEP 002
3
DEP 003
4
Average(DEP 001) = (4+3+3+1)/4 = 11/4 = 2.75
Average(DEP 002) = (2+4+3)/3 = 9/3 = 3
Average(DEP 003) = (1+2+4)/3 = 7/3 = 2.3
Average(DEP 004) = (5+2)/2 = 7/2 = 3.5
MAX(Average(DEP 001), Average(DEP 002), Average(DEP 003), Average(DEP 004)) = 3.5
Result = DEP 004
first let's create the average table
Modelling --> New Table
average table =
ADDCOLUMNS (
SUMMARIZE ( YourTableName, YourTableName[Department ID ] ),
"avg",
CALCULATE (
AVERAGE ( YourTableName[Criteria 1] ),
ALLEXCEPT ( YourTableName, YourTableName[Department ID ] )
)
)
then let's create the card which shows the max
Maximum Average =
VAR _max =
MAX ( 'average table'[avg] )
RETURN
LOOKUPVALUE ( 'average table'[Department ID ], 'average table'[avg], _max )
but, like your first post, if you have more than 1 criteria, you should unpivot and find a solution after the unpivoting ;)
Here is a measure that does this directly without adding bloat in your model:
ID of Max Avg =
VAR _tbl =
SUMMARIZE (
'Table' ,
'Table'[Department ID] ,
"Avg" , AVERAGE ( 'Table'[Criteria 1] )
)
VAR _top =
TOPN (
1 ,
_tbl ,
[Avg] ,
DESC
)
RETURN
CALCULATE (
SELECTEDVALUE ( 'Table'[Department ID] ) ,
_top
)
Here the measure is used in a Card visual using your sample data:
CALCULATE(
SELECTEDVALUE(tbl[Department ID])
,FILTER(
VALUES(tbl[Department ID])
,CALCULATE(AVERAGE(tbl[Criteria 1]))=MAXX(
VALUES(tbl[Department ID])
,CALCULATE(Average(tbl[Criteria 1]))
)
)
)

Calculating percentiles by group in Power BI

Below is a sample data and I am looking for a solution to calculate percentiles (25th, 50th, 75th, 100th) for quantity sold grouped by country.
So basically add countries into different buckets from low, mid 1, mid 2 or high depending upon the unit_quantity. So if I create a table shown below in power bi, I want to create a calculated measure that adds the countries into these different buckets.
Currently, what I have tried is create 3 different quartiles in below dax measure and then using IF function i have tried to put them in different buckets :
Quartile =
var FirstQ =
CALCULATE(
PERCENTILE.INC('Sample Table'[unit_quantity], .25),
ALLSELECTED('Sample Table')
)
var SecondQ =
CALCULATE(
PERCENTILE.INC('Sample Table'[unit_quantity], .5),
ALLSELECTED('Sample Table')
)
var ThirdQ =
CALCULATE(
PERCENTILE.INC(Sample Table'[unit_quantity], .75),
ALLSELECTED(Sample Table')
)
var currentVal = SELECTEDVALUE(Sample Table'[unit_quantity])
return
IF(currentVal <= FirstQ, "Low",
IF(currentVal > FirstQ && currentVal <= SecondQ, "Mid",
IF(currentVal > SecondQ && currentVal <= ThirdQ, "Mid 2", "High")
)
)
But the above measure calculates quartiles for the complete table and not grouped by country. Also I want this to be dynamic since I am going to have a slicer for category column so the percentile values should dynamically change according to the category. I am very new to power BI so please bear with me.
You can use PERCENTILEX to run a table calculation, in this case, all countries.
I've added a condition of ISFILTERED to only display the results if the country field is present.
Calculation
Quartile =
VAR SelectedUnit =
SUM ( 'Table'[unit_quantity] )
VAR p25 =
PERCENTILEX.INC (
ALLSELECTED ( 'Table'[country] ),
CALCULATE ( SUM ( 'Table'[unit_quantity] ) ),
0.25
)
VAR p50 =
PERCENTILEX.INC (
ALLSELECTED ( 'Table'[country] ),
CALCULATE ( SUM ( 'Table'[unit_quantity] ) ),
0.5
)
VAR p75 =
PERCENTILEX.INC (
ALLSELECTED ( 'Table'[country] ),
CALCULATE ( SUM ( 'Table'[unit_quantity] ) ),
0.75
)
RETURN
SWITCH (
ISFILTERED('Table'[country]),
SelectedUnit <= p25, "Low",
SelectedUnit > p25
&& SelectedUnit <= p50, "Mid",
"High"
)
Output
country
Unit Sum
Quartile
Bulgaria
2
Low
Canada
83
High
Croatia
49
Mid
India
75
High
Russia
38
Low
United States
69
High

Number of Persons above the Mean

I have this simple data set from Excel:
Date Person Amount
Jan-18 jason 1
Jan-18 fred 2
Jan-18 george 3
Feb-18 jason 10
Feb-18 fred 12
Feb-18 george 15
Feb-18 jim 25
I added two measures:
Amount = SUM( Data[Amount] )
and
Average Amount per Person =
AVERAGEX(
VALUES( Data[Person]),
[Amount]
)
This works as I expect and is dynamic when I select a specific Date:
What I now want is "Number of Persons Above Average" - so in the screenshot only Jim is above 15.50 so the measure should return 1.
My attempt at this measure is this:
Number of Persons Above Average =
CALCULATE(
DISTINCTCOUNT( Data[Person] ),
FILTER(
Data,
SUM( Data[Amount] ) >= [Average Amount per Person]
)
)
As you can see below it just returns the number of persons displayed - in this case 4
How do I amend the above measure to the correct DAX ?
I like to use variables in situations like these:
Number of Persons Above Average =
VAR AveragePerPerson = [Average Amount per Person]
RETURN
CALCULATE ( DISTINCTCOUNT ( Data[Person] ),
Data[Amount] >= AveragePerPerson )
This way you don't have to worry about how the average measure will be computed inside of the CALCULATE and you don't have to use a FILTER function.

DAX custom Grand Total not behaving as expected

I have a Measure which calculates a cumulative total:
CumulativeCount:=
VAR date1 = MAX( DimDate[Date] )
VAR date2 = MAX( FactTable[EndDate] )
RETURN
CALCULATE (
SUM( FactTable[Count] ),
DimDate[Date] <= date1,
DimDate[Date] <= date2,
ALL( DimDate[Date] )
)
And another, actually used in the Pivot Table, which, when it's calculating the Grand Total, is supposed to add up the cumulative totals for each date:
CumulativeCountForPivot:=
IF (
-- If calculating for one group
COUNTROWS( VALUES( FactTable[Group] ) ) = 1,
-- Do core logic
[CumulativeCount],
-- Else add up the results from each group
SUMX(
VALUES( FactTable[Group] ),
[CumulativeCount]
)
)
I don't understand why the Grand Total in the final column is 12, not 6.
The reason is that the grand total is for GroupA and GroupB combined and there is a cumulative count of 12 on that date (6 for each group).
On 06/01/2017 there are no records for GroupA so the [CumulativeCount] measure is returning a blank, even though there are records before that date and the count would be 6. If you added a record for GroupA with a Count of 0 on 06/01/2017, then you would see 6 appear.
If you want a measure that only shows 6 on that date, then try something like this:
CountForPivot =
VAR TempTable = SUMMARIZE(FactTable,
FactTable[Group],
FactTable[EndDate],
"Cumulative",
CALCULATE(SUM(FactTable[Count]),
FILTER(ALLEXCEPT(FactTable, FactTable[Group]),
FactTable[EndDate] <= MAX(FactTable[EndDate])
)
)
)
RETURN SUMX(TempTable, [Cumulative])