Rank Values Within Groups in Power BI Using DAX: RANKX Guide
Rank Values Within Groups in Power BI, Ranking data is one of the most useful techniques in Power BI. Whether you’re comparing sales representatives, ranking products within categories, or identifying the top-performing players on different teams, a simple overall rank is often not enough.
What if you want to answer a more specific question?
βWhat is this valueβs rank compared only with other values in the same group?β
That is where grouped ranking in DAX becomes extremely useful.
In this article, you’ll learn how to create a calculated column that ranks values independently within each group using the RANKX and FILTER functions.
What Is Grouped Ranking?
Imagine you have basketball player data containing three columns:
- Player
- Team
- Points
You don’t want to rank every player together. Instead, you want each player to be compared only with players from the same team.
For example:
| Player | Team | Points | Rank |
|---|---|---|---|
| Alex | Team A | 39 | 1 |
| John | Team A | 30 | 2 |
| David | Team A | 22 | 3 |
| Mike | Team B | 45 | 1 |
| Sam | Team B | 28 | 2 |
| Tom | Team B | 17 | 3 |
Notice that Mike receives rank 1 even though his 45 points are being compared only with Team B players.
This is different from a normal ranking, where all players would compete against one another.
The DAX Formula for Rank Values Within Groups in Power BI
Suppose your Power BI table is called my_data.
You can create a calculated column using:
Points Rank =
VAR current_team = 'my_data'[Team]
RETURN
RANKX(
FILTER(
'my_data',
'my_data'[Team] = current_team
),
'my_data'[Points],
,
,
SKIP
)
This formula creates a new column called Points Rank and calculates the rank of each player’s points within their own team.
How Does the Formula Work?
At first glance, RANKX combined with FILTER can look complicated. However, the logic is straightforward.
Step 1: Capture the Current Group
VAR current_team = 'my_data'[Team]
The variable stores the team associated with the current row.
For example, if the current player belongs to Team A, then:
current_team = "Team A"
This gives DAX the group against which the current row should be compared.
Step 2: Filter the Table
The next part is:
FILTER(
'my_data',
'my_data'[Team] = current_team
)
This tells Power BI to create a temporary table containing only rows belonging to the current team.
If the current player is from Team A, the ranking calculation sees only Team A players.
Conceptually:
All Players
β
Filter by Current Team
β
Team A Players
β
Rank Their Points
This is the key idea behind grouped ranking.
Step 3: Rank the Values with RANKX
The RANKX function performs the actual ranking:
RANKX(
FILTER(...),
'my_data'[Points],
,
,
SKIP
)
The first argument defines the table to rank.
The second argument specifies the value being ranked:
'my_data'[Points]
The final argument:
SKIP
defines how ties should be handled.
Understanding the Ranking Direction
One important detail in RANKX is the order parameter.
By default, RANKX ranks values in descending order.
That means:
- Highest value β Rank 1
- Second-highest β Rank 2
- Third-highest β Rank 3
For example:
| Player | Team | Points | Rank |
|---|---|---|---|
| Alex | A | 39 | 1 |
| John | A | 30 | 2 |
| David | A | 22 | 3 |
This is usually what you want when ranking performance, sales, scores, revenue, or similar metrics.
Ranking from Lowest to Highest
Sometimes you need the opposite approach.
For example, you might want to rank employees according to the lowest number of defects, lowest processing time, or lowest cost.
In that case, specify 1 as the fourth argument of RANKX:
Points Rank =
VAR current_team = 'my_data'[Team]
RETURN
RANKX(
FILTER(
'my_data',
'my_data'[Team] = current_team
),
'my_data'[Points],
,
1,
SKIP
)
Now the smallest value receives rank 1.
For example:
| Player | Team | Points | Rank |
|---|---|---|---|
| David | A | 22 | 1 |
| John | A | 30 | 2 |
| Alex | A | 39 | 3 |
So remember:
RANKX order = DESC β highest value gets Rank 1
RANKX order = ASC β lowest value gets Rank 1
What Does SKIP Mean?
The final parameter controls how DAX handles tied values.
Consider this example:
| Player | Team | Points |
|---|---|---|
| Alex | A | 40 |
| John | A | 30 |
| David | A | 30 |
| Peter | A | 20 |
With SKIP, the result is:
| Player | Points | Rank |
|---|---|---|
| Alex | 40 | 1 |
| John | 30 | 2 |
| David | 30 | 2 |
| Peter | 20 | 4 |
Notice that there is no rank 3.
This happens because two players share rank 2, so the next available position is rank 4.
If you want different tie behavior, DENSE can be used instead:
Points Rank =
VAR current_team = 'my_data'[Team]
RETURN
RANKX(
FILTER(
'my_data',
'my_data'[Team] = current_team
),
'my_data'[Points],
,
,
DENSE
)
With DENSE, the same example would produce:
| Player | Points | Rank |
|---|---|---|
| Alex | 40 | 1 |
| John | 30 | 2 |
| David | 30 | 2 |
| Peter | 20 | 3 |
There is no skipped rank.
How to Create the Calculated Column in Power BI
To implement this in Power BI Desktop:
- Open your Power BI report.
- Select the table containing your data.
- Go to Table tools.
- Select New column.
- Enter the DAX formula.
- Press Enter.
- Power BI will create the ranking column.
For example:
Points Rank =
VAR current_team = 'my_data'[Team]
RETURN
RANKX(
FILTER(
'my_data',
'my_data'[Team] = current_team
),
'my_data'[Points],
,
,
SKIP
)
You can then add Player, Team, Points, and Points Rank to a table visual to verify the results.
A Practical Example Beyond Basketball
Grouped ranking isn’t limited to sports data.
Suppose you’re analyzing company sales:
| Salesperson | Region | Sales |
|---|---|---|
| A | North | 500000 |
| B | North | 420000 |
| C | North | 350000 |
| D | South | 600000 |
| E | South | 450000 |
| F | South | 300000 |
You could use the same technique to determine the salesperson’s position within their region.
Simply replace the columns:
Sales Rank =
VAR current_region = 'my_data'[Region]
RETURN
RANKX(
FILTER(
'my_data',
'my_data'[Region] = current_region
),
'my_data'[Sales],
,
,
SKIP
)
The same pattern can be applied to:
- Sales by region
- Products by category
- Employees by department
- Students by class
- Customers by country
- Stores by territory
- Products by brand
- Revenue by business unit
- Performance by team
The General Pattern to Remember
The most important concept is not the basketball example. It is the reusable DAX pattern:
Rank =
VAR current_group = 'Table'[GroupColumn]
RETURN
RANKX(
FILTER(
'Table',
'Table'[GroupColumn] = current_group
),
'Table'[ValueColumn],
,
,
SKIP
)
You can think of it as:
Identify the current group β filter to that group β rank the desired value.
Once you understand this pattern, you can adapt it to almost any grouped-ranking problem in Power BI.
Final Takeaway
A standard rank tells you where a value stands across the entire dataset. A grouped rank tells you where that value stands among comparable records.
The combination of VAR, FILTER, and RANKX provides a simple way to achieve this in DAX.
If you need the highest value to receive rank 1, use the default descending order. If you need the lowest value to receive rank 1, use 1 as the order argument. And if your data contains ties, choose between SKIP and DENSE depending on whether you want ranking positions to be skipped.
Once you master this technique, you can build more meaningful Power BI reports where every employee, product, customer, or business unit is evaluated within the group that actually matters.