Greg_Deckler
6 years agoCommunity Champion
TRIMMEAN
When you think you pick an easy one...
In my recent quest to create or catalog as many DAX equivalents for Excel functions, I figured this one would be a cinch. Well, not so much. Between poor ...
CL2316
3 years agoFrequent Visitor
Hi there, thanks so much for posting this!
Would you be able to add some commentary on what each variable is doing/why and how? I'm a PowerBI newbie and would really like to understand each part of that measure and how it calculates the trimmed mean.
Thanks so much
Greg_Deckler
3 years agoCommunity Champion
CL2316 Sure, see below. It may help to use the PBIX and to return each table variable as a table to see what is in the table at each stage of the calculation.
TRIMMEAN =
// First, take the table and add a Rank column, 1 to however many values are in the table
VAR __Table =
ADDCOLUMNS(
'Table',
"Rank",RANKX('Table',[Value])
)
// This is the percentage to trim off (20%)
VAR __Percent = .2
// Count the number of rows in the table
VAR __Count = COUNTROWS(__Table)
// Figure out how many rows to trim off The fractional number of data points to exclude
// from the calculation. For example, if percent = 0.2, 4 points are trimmed from a data
// set of 20 points (20 x 0.2): 2 from the top and 2 from the bottom of the set.
VAR __Trim = MROUND(__Count * __Percent,2) / 2
// Get the highest rank
VAR __MaxRank = MAXX(__Table,[Rank])
// Get the lowest rank
VAR __MinRank = MINX(__Table,[Rank])
// OK if I recall, it was necessary to account for having more than one row with the same
// rank so some of this gets a little complicated. But, basically, take the virtual
// __Table variable and and group by Rank column, adding a Count column for how many
// rows have that rank and what the value of that rank is. Add cumulative counter columns
// that simply count how many rows have a higher or lower rank than the current row. In
// addition, add our while loop counters that take the __Trim variable and subtract our
// cumulative counters.
VAR __RanksTable =
ADDCOLUMNS(
ADDCOLUMNS(
GROUPBY(
__Table,
[Rank],
"Count",COUNTX(CURRENTGROUP(),[Value]),
"Value",MAXX(CURRENTGROUP(),[Value])
),
"CumulativeBottomCount",COUNTROWS(FILTER(__Table,[Rank] >= EARLIER([Rank]))),
"CumulativeTopCount",COUNTROWS(FILTER(__Table,[Rank] <= EARLIER([Rank])))
),
"BottomWhile",__Trim - [CumulativeBottomCount],
"TopWhile",__Trim - [CumulativeTopCount]
)
// The minimum bottom row (rank) that we want is the highest rank where the bottom while
// loop counter is <= 0. So if __Trim is 2 then the bottom while counter goes to 0 at
// three up from the lowest rank.
VAR __MinBottom = MAXX(FILTER(__RanksTable,[BottomWhile]<=0),[BottomWhile])
VAR __MinTop = MAXX(FILTER(__RanksTable,[TopWhile]<=0),[TopWhile])
// This table is for the items that we want to exclude (bottom)
VAR __FinalBottomRankTable =
ADDCOLUMNS(
FILTER(__RanksTable,[BottomWhile]>=__MinBottom),
"Product",IF([BottomWhile]>=0,[Count]*[Value],([Count] + [BottomWhile]) * [Value])
)
// This table is for the items that we want to exclude (top)
VAR __FinalTopRankTable =
ADDCOLUMNS(
FILTER(__RanksTable,[TopWhile]>=__MinTop),
"Product",IF([TopWhile]>=0,[Count]*[Value],([Count] + [TopWhile]) * [Value])
)
// Add up the values to exclude
VAR __Bottom = SUMX(__FinalBottomRankTable,[Product])
VAR __Top = SUMX(__FinalTopRankTable,[Product])
RETURN
// Determine the average by summing the values in __Table, subtract the bottom and top
// and then divide by the number of rows minus 2 * the number of rows to trim from top
// and bottom.
DIVIDE(
SUMX(__Table,[Value]) - __Bottom - __Top,
__Count - 2 * __Trim
)
- CL23163 years agoFrequent Visitor
Wow thank you so much for this, it is beyond helpful!
I'm struggling with calculating the trimmed mean whilst filtering a table:VAR __Order =ADDCOLUMNS(SUMMARIZE(CALCULATETABLE( '280CT_order', '280CT_order_detail'[product category] = "Donation"),'280CT_order'[supporterid],"giftperhead", CALCULATE( [£ Gross Income],'280CT_order_detail'[product category] = "Donation")) ,"Rank", ( RANKX('280CT_order', [giftperhead])))I'm trying to filter the Order table to people who have only given a donation and make sure it's 1 row per supporter if they have given multiple donations. Please can you help?