2
votes

How to calculate rank within Category defined on sales level. Say, that we want to label products with Sales above some threshold with Category "high", and below that threshold with Category "low".

Here is a sample data.

let
    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WcisqzSwpVtJRSiwoyEkF0oZKsTpIwkmJeUAIZJigipfn56QlpRYVVQLZpqhSyRlQcWOweFhqempJYlJOKlgusagovwS7XEF+SWJJPtwJKHL5eZn5eUDaHNUqHI5GdkEsAA==", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type text) meta [Serialized.Text = true]) in type table [Category = _t, Product = _t, Amount = _t]),
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"Amount", Int64.Type}})
in
    #"Changed Type"

My question is a nephew related to its older uncle, who now I want to call in:

Percent Rank within Category = 

VAR HasOneValueTrue = HASONEVALUE ( MyTable[Product] )

VAR tbl =
    CALCULATETABLE (
        VALUES ( MyTable[Product] ),          
        REMOVEFILTERS ( MyTable[Product] ),   
        VALUES ( MyTable[Category] )          
    )

VAR result =
    CALCULATE (
        DIVIDE (
            RANKX (
                tbl,
                [Sales], 
                ,
                ASC
            ) - 1,
            COUNTROWS ( tbl ) - 1
        )
    )
RETURN 
    IF (
        HasOneValueTrue,
        result
    )

The difference is that the uncle has Category defined in table column, but now we want to have category calculated on the fly based on sales level. So I tried replacing the VAR tbl code with the following one with the threshold of 4:

var tbl =
SUMMARIZECOLUMNS (
    MyTable[Product],
    "CalculatedCategory", IF ( [Sales] > 4, "high", "low" ),
    "AggSales", [Sales]
)

Nevertheless, I am not able to refer to such defined variable. I also failed with trial based on creating first a DAX table and then trying to refer to it.

Here are expected results:

expected results

References

Here is the family of related questions which members I met on the way while approaching to state this problem.

DAX equivalent of Excel PERCENTRANK.INC per category

DAX RANKX for within Category

DAX REMOVEFILTERS vs ALL

The value parameter in DAX function RANKX

DAX ALLEXCEPT to sum by category of multiple dimension tables

2
Can you show what you want your result to look like?Alexis Olson
@AlexisOlson Expected results is served!Przemyslaw Remin

2 Answers

1
votes

This can be done with a minor modification to my answer here. Copied below:

Percent Rank =
VAR ProductsInCategory =
    CALCULATETABLE (
        VALUES ( MyTable[Product] ),
        ALLSELECTED ( MyTable[Product] )
    )
VAR RankProduct = RANKX ( ProductsInCategory, [Sales],, ASC )
RETURN
    IF (
        HASONEVALUE ( MyTable[Product] ),
        DIVIDE ( RankProduct - 1, COUNTROWS ( ProductsInCategory ) - 1 )
    )

First, define the calculated category as you suggested.

CalculatedCategory = IF ( [Sales] > 4, "high", "low" )

Then plug that into a filter in the ProductsInCategory variable.

Exp. Results =
VAR CalculatedCategory = [CalculatedCategory] /*Determine current category*/
VAR ProductsInCategory =
    CALCULATETABLE (
        VALUES ( MyTable[Product] ),
        FILTER (
            ALLSELECTED ( MyTable[Product] ),
            [CalculatedCategory] = CalculatedCategory /*New Condition*/
        )
    )
VAR RankProduct = RANKX ( ProductsInCategory, [Sales],, ASC )
RETURN
    IF (
        HASONEVALUE ( MyTable[Product] ),
        DIVIDE ( RankProduct - 1, COUNTROWS ( ProductsInCategory ) - 1 )
    )

Output:

Matrix Visual


Edit:

To handle the case where there is only 1 product in a category, you can use MAX to disallow a zero value for the denominator.

Exp. Results =
VAR CalculatedCategory = [CalculatedCategory] /*Determine current category*/
VAR ProductsInCategory =
    CALCULATETABLE (
        VALUES ( MyTable[Product] ),
        FILTER (
            ALLSELECTED ( MyTable[Product] ),
            [CalculatedCategory] = CalculatedCategory /*New Condition*/
        )
    )
VAR RankProduct = RANKX ( ProductsInCategory, [Sales],, ASC )
RETURN
    IF (
        HASONEVALUE ( MyTable[Product] ),
        DIVIDE (
            RankProduct - 1,
            MAX ( COUNTROWS( ProductsInCategory ) - 1, 1 )
        )
    )
0
votes

Being very grateful to Alexis Olson, I would like to share a different solution I ended up with. The solution proposed by Alexis works well in my simple example, but it did not work in my complex model. In my complex model the RANKX function does not give the expected results. RANKX returns the same rankings for different sales values.

For the time being this is the solution that works without figuring out what causes RANKX to return ties for different sales values.

First of all, defining Category measure:

CalculatedCategory = 
SWITCH (
    TRUE (),
    NOT ( HASONEVALUE ( MyTable[Product] ) ), "total", -- important to handle totals
    [Sales] <= 4, "low",
    [Sales] >  4, "high",
    "other"
)

It is important to exclude totals from Category. I did it by setting up a different category for totals. Otherwise totals will fall into "high" category bucket. It would distort final results.

I have not used RANKX in calculation of Percent Rank within Category. I used MIXTURE OF COUNTROWS and FILTER.

PercentRank within Category = 
VAR category = [CalculatedCategory]
VAR ProductSales = [Sales]
VAR ProductsMatching = 
COUNTROWS (
            FILTER (
                ALLSELECTED ( MyTable[Product] ),
                [CalculatedCategory] = category
                    && [Sales] >= ProductSales
            )
        )
var ProductsAll = 
COUNTROWS (
            FILTER (
                ALLSELECTED ( MyTable[Product] ),
                [CalculatedCategory] = category
            )
        )

RETURN
    DIVIDE (ProductsMatching-1, MAX( ProductsAll-1, 1 ))

I calculated rows of two tables. First table ProductsMatching has all products that have sales in appropriate category and sales that are higher or equal of the product. ProductsAll returns number of products in category.