Forum Discussion
Handle AverageX on different granularities matrix drilldown
Hello,
I have a matrix in Power BI where you can drill down to different granularities, from region on top to customer level (3 levels).
The problem is that when drilling down, my averageX is not correct on total level as it takes the same values as the one on top. I have started a DAX query to make it dynamic:
This is the base measure to have the values at row/column level as I'm using th
_DivideAccuracy =
VAR actuals = CALCULATE ( SUM ( 'FACT_ActualsBudget'[Actuals] ) )
VAR forecast = CALCULATE ( SUM ( FACT_Forecast[Forecast] ) )
VAR TempCheck =
MAX ( actuals, forecast )
VAR TempAccuracy =
DIVIDE ( ABS ( actuals - forecast ), TempCheck, 0 )
VAR CheckIfNull =
COALESCE ( actuals, -1 )
RETURN
IF (
CheckIfNull <> -1
&& AND ( actuals = 0, forecast = 0 ),
1,
IF ( CheckIfNull <> -1, 1 - TempAccuracy )
)
This is the measure to do the averageX on total level. As you can see, the values_rows variable does now on CustomerRegion level but how do I do this for other granularities. When using ISINSCOPE or ISFILTERED is difficult because on total level on column level we don't know if it is on region or sales person level.
_DynamicMeasureAccuracy =
VAR values_rows =
VALUES ( DIM_Customer[CustomerRegion] )
VAR values_columns =
VALUES ( DIM_CalendarDate[MonthName] )
VAR all_combinations =
CROSSJOIN ( values_rows, values_columns )
RETURN
AVERAGEX ( all_combinations, CALCULATE ( [_DivideAccuracy] ) )
It should be:
This is a simple example I want but maybe it is more difficult with the base measure I wrote:
Region Salesperson January
APR A 5
APR B 10
APR C 4
APR D 8
AverageX = 6.75
Region January
APR 27
AverageX = 27
Kind regards
Kevin
Hello,
The question has not been resolved but I have found a workaround using bookmarks and seperate measures for each level of hierarchy. I guess the problem lies in the hierarchy context for the totals for which a single measure is "impossible" to use.
Kind regardsKevin
21 Replies
- King7son1Frequent Visitor_DynamicMeasureAccuracy =VAR HasRegion = ISINSCOPE( 'Region'[Region] )VAR HasSalesPerson = ISINSCOPE( 'SalesPerson'[SalesPerson] )RETURNSWITCH( TRUE(),-- 1. DETAIL ROW (SalesPerson)-- If we are on a salesperson row, just run the standard accuracy math.HasSalesPerson,[_DivideAccuracy],-- 2. REGION ROW (The Fix)HasRegion,AVERAGEX(VALUES('SalesPerson'[SalesPerson]),[_DivideAccuracy]),-- 3. GRAND TOTAL-- If we are at the total, Average the Regions (which are averages of salespeople).AVERAGEX(VALUES('Region'[Region]),CALCULATE(AVERAGEX(VALUES('SalesPerson'[SalesPerson]),[_DivideAccuracy]))))
- King7son1Frequent Visitor#AverageAccuracyCorrectedV1 =-- Define the Virtual Table with hierachy and AccuracyVAR _VirtualTable =ADDCOLUMNS(ADDCOLUMNS(ADDCOLUMNS(ADDCOLUMNS(DISTINCT(UNION(SUMMARIZE(FACT_FORECAST,FACT_FORECAST[MonthKey],FACT_FORECAST[ProductGroupKey],FACT_FORECAST[SalesPersonKey]),SUMMARIZE(FACT_SALES,FACT_SALES[MonthKey],FACT_Sales[ProductGroupKey],FACT_SALES[SalesPersonKey]))),"_Actual" , CALCULATE( SUM(FACT_SALES[Sales]), FACT_SALES[MonthKey] = EARLIER([MonthKey]) , FACT_SALES[ProductGroupKey] = EARLIER([ProductGroupKey]), FACT_SALES[SalesPersonKey] = EARLIER([SalesPersonKey] )) ,"_Forecast" , CALCULATE( SUM(FACT_FORECAST[Forecast]), FACT_FORECAST[MonthKey] = EARLIER([MonthKey]) , FACT_FORECAST[ProductGroupKey] = EARLIER([ProductGroupKey]), FACT_FORECAST[SalesPersonKey] = EARLIER([SalesPersonKey] ))),"_Diff" , ABS([_Actual] -[_Forecast]),"_Denom" , MAX([_Actual], [_Forecast])),"_Error" , DIVIDE ([_diff] , [_Denom],0)),"Accuracy", 1 - [_Error])RETURNSWITCH(TRUE(),-- [CASE 1] Lowest Level: Product Group-- If we are looking at a specific Product, calculate the simple math directly.ISINSCOPE(DIM_PRODUCTGROUP[ProductGroupKey]) || ISINSCOPE(DIM_SALESPERSON[SalesPersonKey]),[#DivideAccuracy],-- [CASE 2] Totals & Subtotals (Default)-- If we are on a SalesPerson Total, Region Total, or Grand Total,-- calculate the AVERAGE of the rows in the Virtual Table.AVERAGEX(_VirtualTable, [Accuracy]))
- amitchandak
Super User
naelske_cronos , You can use isinscope and do different calculations =. This should work from leaf (Lowest) level to higher
Switch( True(),
isinscope(DIM_CalendarDate[MonthName] ), _DivideAccuracy,
isinscope(DIM_Customer[CustomerRegion]), AverageX(values(DIM_CalendarDate[MonthName] ), _DivideAccuracy))
and so on
IsInScope - Switch Rank at different levels: https://youtu.be/kh0gezKICEM
How to Switch Subtotal and Grand Total in Power BI | Power BI Tutorials| isinscope: https://youtu.be/smhIPw3OkKA- naelske_cronos
Advocate II
Hello amitchandak,
I did like you told me too. This is my code:
_DynamicMeasureAccuracy2 = VAR values_columns = VALUES ( DIM_CalendarDate[MonthName] ) RETURN SWITCH ( TRUE (), ISINSCOPE ( DIM_CalendarDate[MonthName] ), [_DivideAccuracy], ISINSCOPE ( DIM_Customer[CustomerRegion] ), AVERAGEX ( values_columns, [_DivideAccuracy] ), ISINSCOPE ( DIM_Customer[CustomerSalesPersonDescr] ), AVERAGEX ( CROSSJOIN ( VALUES ( DIM_Customer[CustomerRegion] ), values_columns ), [_DivideAccuracy] ), AVERAGEX ( CROSSJOIN ( VALUES ( DIM_Customer[CustomerSalesPersonDescr] ), values_columns ), [_DivideAccuracy] ) )As you can see, the grand total for region is not correct because it is it 78.52% instead of 91.59%. That is because it checks the last condition of the switch for sales person:
On sales person the grand total is correct but the subtotals are not correct as for january it should be 85.84%
I guess it is a good base with the ISINSCOPE but how do my subtotals change based on the filter context if region or if salesperson => region is 1 row so for january 89.61% / 1 = 89.61% but when on sales person (74.02% + 96.23% + 75.06% + 100.00% + 78.94% + 96.65% + 80.00%) / 7 = 85.84%
Thanks
Kind regards
Kevin
- amitchandak
Super User
naelske_cronos , add one condition when both are not in scope, all rows and columns
- King7son1Frequent Visitor
As Amit said you can use INSCOPE to perform different calculations
DynamicMeasureAccuracy =
VAR HasRegion = ISINSCOPE( 'Region'[Region] )
VAR HasSalesPerson = ISINSCOPE( 'SalesPerson'[SalesPerson] )
RETURN
SWITCH( TRUE(),
-- 1. DETAIL ROW (SalesPerson)
-- If we are on a salesperson row, just run your base measure
HasSalesPerson,
[_DivideAccuracy],
-- 2. REGION ROW (The Fix)
-- If we are on a Region row, we can’t just use base measure
-- Instead, iterate through the Salespeople and average their individual scores
HasRegion,
AVERAGEX(
VALUES('SalesPerson'[SalesPerson]),
[_DivideAccuracy]
),
-- 3. GRAND TOTAL
-- If we are at the total, Average the Regions (which are averages of salespeople).
-- Or change this if you want average across every salesperson ignoring region ( AVERAGEX(VALUES(‘SalesPerson’[SalesPerson]), [_DivideAccuracy]) ….)
AVERAGEX(
VALUES('Region'[Region]),
CALCULATE(
AVERAGEX(
VALUES('SalesPerson'[SalesPerson]),
[_DivideAccuracy]
)
)
)
)
- naelske_cronos
Advocate II
Hello,
Well I tried this DAX code on my example that I showed instead of the complex one:
ExcelStyle = VAR RowIsDetail = ISINSCOPE(SalesPerson[Key]) VAR region = ISINSCOPE(Region[Region]) VAR ColIsDetail = ISINSCOPE('Date'[Month]) RETURN SWITCH( TRUE(), -- DETAIL CELL: salesperson + month → show sum ColIsDetail && (RowIsDetail || region), [SumValue], -- ROW SUBTOTAL (region) → show average NOT RowIsDetail && ColIsDetail, average('FACT'[Value]), -- COLUMN TOTAL (month total row) → show average RowIsDetail && NOT ColIsDetail, average('FACT'[Value]), -- GRAND TOTAL (neither in scope) → show average average('FACT'[Value]) )but the problem is that the total does not know when to use regional level or sales person level because there is no filter context on total row, so still no luck...
Kind regards
Kevin
- King7son1Frequent Visitor
naelske_cronos to be honest I am truly confused about the requirements.
It is hard to provide assistance when you are changing from one model design to another in your responses
Do you need to need a weighted average based on the logic shown in your _DivideAccuracy measure or just a straight average (ex, Divide(fActual, fForecast,0) ?
Are building from one fact table or two? (perhaps you should provide a screen shot of model view or even better create measures to document you model and share that (INFO.VIEW.COLUMNS function (DAX) - DAX | Microsoft Learn) as well as some sample data.
INFO.VIEW.MEASURES()INFO.VIEW.COLUMNS()
INFO.VIEW.RELATIONSHIPS()Also confirm the hieracy for your visual. You only need to use insocpe for the items on your hierachy, Regison SalesPerson (the fields you drill into ) .
- King7son1Frequent Visitor
Please let us know if this solution works. Also, for a more performant solution try the following. I was not able to save the file in GIT as I did not have permissions
#AverageAccuracyCorrected =--[1]Instead of DISTINCT(UNION pattern create gain by grouping dimension columns. This has a significant performance boost vs scanning the fact tables-- Because we are using Dimension columns this virtual table should maintain lineage to the Dimensions.VAR _Grain =DISTINCT(UNION(-- 1. Get keys from Forecast,SUMMARIZE(FACT_FORECAST,DIM_DATE[MonthKey],DIM_PRODUCTGROUP[ProductGroupKey],DIM_SALESPERSON[SalesPersonKey]),-- 2. Get keys from Sales,SUMMARIZE(FACT_SALES,DIM_DATE[MonthKey],DIM_PRODUCTGROUP[ProductGroupKey],DIM_SALESPERSON[SalesPersonKey])))-- [2] now add the facts for sales and forecastVAR _TableWithMetrics =ADDCOLUMNS(_Grain,"@Actual", [#SumSales], -- Measure with CALCULATE(SUM(...))"@Forecast", [#SumForecast] -- Measure with CALCULATE(SUM(...)))-- [3] Add the Math (Accuracy logic)-- We create a new variable so we can reference [@Actual] and [@Forecast] created in the previous stepVAR _TableWithAccuraryLogic =ADDCOLUMNS(_TableWithMetrics,"@Diff", ABS( [@Actual] - [@Forecast] ),"@Denom", MAX( [@Actual], [@Forecast] ),"@AccuracyRaw",VAR _Diff = ABS( [@Actual] - [@Forecast] )VAR _Denom = MAX( [@Actual], [@Forecast] )RETURNIF( _Denom = 0, 0, 1 - DIVIDE( _Diff, _Denom ) ))RETURNSWITCH(TRUE(),-- [CASE 1] Lowest Level: Product Group or salesperson-- If we are looking at a specific SalesPerson or Product complete the accuaracy mathISINSCOPE(DIM_PRODUCTGROUP[ProductGroupKey]) || ISINSCOPE(DIM_SALESPERSON[SalesPersonKey]),[#DivideAccuracy],-- [CASE 2] Totals & Subtotals (Default)-- If we are on a SalesPerson Total, Region Total, or Grand Total,-- calculate the AVERAGE of the rows in the Virtual Table.AVERAGEX(_TableWithAccuraryLogic, [@AccuracyRaw]))- naelske_cronos
Advocate II
Hello,
Thank you for your dedication on working on this particular problem and I see what you are trying to do. If I'm not mistaken you are calculating the accuracy of each row seperately and then doing an average.
For example for APR => George Jefferson => January I have 71.10% and you have 30.30%.
It is another way of calculating but the most important part is that the subtotals remains the same for each level in the hierarchy and that is what I am also trying to change. I still don't have the result as desired. I don't think it is possible to have some kind of hierarchy context filter on (sub)total level because this is where the dynamic change is important based on the current hierarchy.
A workaround is to work with seperate measures and bookmarks. It is an ugly way to workaround but I don't think there is another short term solution.
Thank you
Kind regards
Kevin
- King7son1Frequent Visitor
The measures are not working as expected because in some DIM and Fact tables you have the key as text and in some it is a whole number. These need to be the same. Since you are not changing the calculation at different granularity you do not need INSCOPE(). You just need an iterator. Also, because Region is a column in SlaesPerson you do not need to iteratrate over this level of the hierachy. You can use one measure for all visuals.
#AverageAccuracyCorrected =--get unique combo SalesPerson & Product for SalesVAR _SalesKeys =SUMMARIZE('FACT_SALES','FACT_SALES'[SalesPersonKey],'FACT_SALES'[ProductGroupKey])--get unique combo SalesPerson & Product for ForecastVAR _ForecastKeys =SUMMARIZE('FACT_FORECAST','FACT_FORECAST'[SalesPersonKey],'FACT_FORECAST'[ProductGroupKey])-- Combine them into one consolidated list of keys and removes duplicates. This is just incase the salespeople in both tables don't matchVAR _AllKeys =DISTINCT( UNION( _SalesKeys, _ForecastKeys ) )-- Map these raw keys to your the keysVAR _VirtualGranularity =TREATAS(_AllKeys,'DIM_SALESPERSON'[SalesPersonKey], -- Handles Region/SalesPerson hierarchy'DIM_PRODUCTGROUP'[ProductGroupKey] -- Handles the Product)RETURN-- Iterate over the Virtual TableAVERAGEX(_VirtualGranularity,[#DivideAccuracy])- naelske_cronos
Advocate II
Hello,
The keys were indeed in text format and I changed them but it didn't change anything to the measures. I did the calculations in EXCEL and the averages seems correct to me.
I have added additional matrices with your formulas but the values don't seem to be the same with one formula, unfortunately. You can find it attached in the Github link I provided you earlier.
Mine is INITIAL and yours is CORRECTED. When doing the math:
- January = (16,91% + 71,10% + 91,25% + 73,96% + 9,11%) / 5 = 52.47% (as blanks are ignored)
- APR = Abraham Lincoln = (16,91% + 92.04%) / 2 = 54.48% (as blanks are ignored)
I don't know why your formula shows a different output.
Thanks
Kind regards
Kevin
- v-nmadadi-msft
Community Support
Hi naelske_cronos
I wanted to check if you had the opportunity to review the valuable information provided by King7son1 Please feel free to contact us if you have any further questions.
Thank you. - v-nmadadi-msft
Community Support
May I check if this issue has been resolved? If not, Please feel free to contact us if you have any further questions.
Thank you- naelske_cronos
Advocate II
Hello,
The question has not been resolved but I have found a workaround using bookmarks and seperate measures for each level of hierarchy. I guess the problem lies in the hierarchy context for the totals for which a single measure is "impossible" to use.
Kind regardsKevin
- v-nmadadi-msft
Community Support
Hi naelske_cronos ,
We really appreciate your efforts and for letting us know the update on the issue.
Please continue using fabric community forum for your further assistance.
Regards