DAX (Power BI) – Dynamic TopN made easy with What If Parameter
In this post I am going to demonstrate how to create a Dynamic TopN slicer using a What-If Parameter.
This will allow your user to simply use the Slicer/Slider to view the TopN values and as a bonus if the user slides it to zero, it will display everything!
Who doesn’t like something that is easy to create, but makes it so much easier for the user to gain insights into their own data easily and quickly?
Example
For this example, I am going to be using a [Sales Amount] measure from my Orders table.
Next for the TopN, I am going to be looking for the TopN by City. This is a key component when identifying what you want your TopN to be based on.
NOTE: If the explanation of the TopN Measure can be a bit complex, if that is the case, you can either copy the code and modify it for your requirements. Or you can view the animated GIF further below so you can see it working.
Creating the TopN Slicer
-
In Power BI Desktop I went to the Modeling tab and then clicked on new Parameter
-
I then gave it the following properties as shown below.
- One thing to NOTE is that I set the Minimum to 0 (zero)
- When you are creating this TopN What-if parameter, you can change anything for the Maximum, Increment and Default.
-
I could then see the table created on the right-hand side.
-
As well as the Slicer on my reporting canvas, which I formatted
Creating the TopN Measure
- Below is the TopN measure, which I will go into detail how it works below.
TopN City = VAR SelectedTop = SELECTEDVALUE('TopN'[TopN]) RETURN SWITCH(TRUE(), SelectedTop = 0, [Sales Amount], RANKX ( ALLSELECTED( 'Orders'[City] ), [Sales Amount] ) <= SelectedTop, [Sales Amount] )
-
Line 2, is where I created the Only Variable called SelectedTop
- This is getting the selected value from the Slicer.
- If it is slid to 5, this variable SelectedTop will store 5.
-
Line 4 is where I used the SWITCH(TRUE()
- What this does, is it enables me to pass multiple statements to evaluate in one DAX function.
- NOTE: You could possibly do this with an IF statement, but I prefer doing it this way, because quite often the requirement changes to have more than 2 conditions, so doing in this way it is easy for me to add another condition.
-
Line 5 is my first condition, where I have said if the TopN Slicer (SelectedTop)= 0 (zero) then display all the [Sales Amount]
- It will do this because there is no filter context being applied on the [Sales Amount]
-
Lines 6 – 9 is where the Magic happens and uses the values from the TopN slicer.
- This is also the second condition for the SWITCH(TRUE()
DAX expression. -
Even though this is for TopN values, I use the RANKX to achieve the desired result from Line 6
- I got this DAX Pattern from SQLBI.COM – Use of RANKX in Power BI measures
- Next, I am using the ALLSELECTED, because in my table I want to select the TopN for the City values.
- Line 7 is where I am selecting from my table Orders and the column called City ALLSELECTED( ‘Orders'[City] ),
- Line 8, is where I am specifying my measure for the RANKX which is the [Sales Amount]
- Line 9, is where I am closing off the RANKX function.
- This is also the second condition for the SWITCH(TRUE()
-
Line 10 is where I am now comparing it to be less than equal to the selected slicer value SelectedTop
- If this evaluates to TRUE, then display the RANKX up to and including the selected slicer value.
- Line 11 is the ELSE condition for the SWITCH(TRUE()
- Line 12 is closing off the SWITCH DAX Function
TopN Slicer in Action
Please watch the following animation below in which I will demonstrate by using the Slider I can to from Top 10, to Top 5. And then by sliding it to 0 (zero) it will show all the cities.
Conclusion
As I have shown, by using the What-If Parameter and some DAX there is now a way to easily create a dynamic TopN that is easy for your users to use within their reports.
As always, if there are any questions or comments please leave them in the section below.
Hi, Gilbert, nice post!
little remark about
— Line 11 is the ELSE condition for the SWITCH(TRUE()
it is the second result of rankx condition as far as I understand, not the ELSE section.
And another question about totals of this TopN measure. It shows complete grandtotal of all cities, not only those, that were selected. Is there any way to make it shows total of selected cities? Something like visualtotals in mdx, don’t know if there is anything in dax.
Hi there, thanks for the comment.
Line 11 is actually the ELSE condition for the SWITCH Statement which it has to have. The reason it looks like it possibly should be for the RANKX, but the RANKX is actually closed off on line 9.
And Line 10 is where we are doing the comparitive to the RANKX output.
You are 100% correct, and this happens due to filter context in Power BI when using the Matrix.
In order to get it working properly this is the updated measure that I have used.
TopN City Matrix =
IF (
COUNTROWS ( VALUES ( ‘Orders'[City] ) ) = 1,
[TopN City]
,
SUMX (
VALUES ( ‘Orders'[City] ),
[TopN City]
)
)
I have also updated the GIF animation to reflect the changes. Thanks for pointing that out to me.
Right, now totals are correct, nice )
But I disagree with you about else section. Let’s see at the switch clause:
SWITCH(, , [, , ]…[, ])
As you see, ELSE is not required section.
SWITCH(TRUE(), —
SelectedTop = 0, –value1
[Sales Amount], –result1
RANKX ( ALLSELECTED( ‘Orders'[City] ), [Sales Amount] ) <= SelectedTop, –value2
[Sales Amount] –result2
–no else section here
)
If you would add something in ELSE position you would get all cities in your report. TopN cities would show their sales amount while others an else section value.
Because you have no else section those cities out of topN get blank and therefore pbi hides them.
o-oh, looks like it has eaten text in >, < – brackets
With the What-if Parameter, it would be really easy to turn this into a TopN or BottomN … with a slight change to the RANKX function.
By setting the Minimum to say -20 that any number below zero can be interpreted as a BottomN. Interpreting whether the SelectedTop is positive or negative, will determine whether you want a TopN or BottomN.
At present there is a catch … you cannot pass a Variable that is based on an IF statement to the Order argument in the RANKX function. It will only accept 1/0 or DESC/ASC … but not a variable that contains these.
It is still possible to get the desired result … but it involves a lot of unnecessary coding as in the following …
Customer Sales Rank =
VAR NoOfCust =
ABS ( [Top Customers Number] )
VAR VRankTop =
RANKX ( ALL ( Customers[Cust Name] ) , [Total Sales] ,, DESC )
VAR VRankBot =
RANKX ( ALL ( Customers[Cust Name] ) , [Total Sales] ,, ASC )
VAR Ranking =
IF ( HASONEVALUE( Customers[Cust Name] ),
IF ( [Top Customers Number] > 0, VRankTop, VRankBot ),
BLANK()
)
RETURN
IF (Ranking > NoOfCust, BLANK(), Ranking)
I can’t imagine that making the necessary enhancemment to RANKX would be very time consuming … and there is a lot of downstream benefit.
That is one day of doing it, and sometimes there is no easy way to make something appear so simple to the end user, but in the actual code it is a lot more difficult.
That is a great way to get it done!
What is the best forum to raise a request on DAX Development Team for making the Order argument accept input from a Variable? The MVP Summit???
If a variable was acceptable then the “TopN or BottomN” Measure could be as simple as the following …
Customer Sales Rank =
VAR NoOfCust =
ABS ( [Top Customers Number] )
VAR ROrder =
IF [Top Customer Number] NoOfCust, BLANK(), Ranking)
Hi Ted. I would suggest going to https://ideas.powerbi.com and search for the idea. If it’s not there then create a new idea
Good Day ,
I need help, I’m new in this Power BI platform.
I like to know where to put this code?
TopN City =
VAR SelectedTop = SELECTEDVALUE(‘TopN'[TopN])
RETURN
SWITCH(TRUE(),
SelectedTop = 0, [Sales Amount],
RANKX (
ALLSELECTED( ‘Orders'[City] ),
[Sales Amount]
)
<= SelectedTop,
[Sales Amount]
)
WHERE Does this "[Sales Amount]" come from ?
Help me please.
Hi there, apologies for not putting that into the blog post.
The [Sales Amount] is a measure that is made up from SUM(‘Table Name'[Sales]
You could substitute the above where it says [Sales Amount]
Hi Gilbert
Its really a nice post. Assume if we have to rank all these by sales then its working fine but when we drag any other column in a bar chart or a matrix it doesn’t show the proper breakdown of individual sales rather it takes the max of for those months based on the selection for TOP N values in the slicer. Please help on this,.
Hi there,
That would be the case because the current measure has got the ALLSELECTED on one column only.
What you could do is to change it to your column or add it as an addition column in the ALLSELECTED?
Hi: I would like to know if someone can help here. Is there a way to add another measure in the table mantaining the top N? For example I have Sales but I have Units also. I want to use my TopN as we have here but adding Units based on the TopN cities.. Anyone can help please?? I really appreciate it.
Best Regards.
Erica
Hi Erica,
This most certainly can be done, and it is in fact a great blog post idea.
If you need something urgently please reach out, otherwise it will be in a blog post in the next week or two.
good example, however i would like to take it further, what if the selection is between, e.g. between 3-9, how to modify the code? cheers
Thanks for the comment.
When you say between 3 and 9 would that be the start and end range?
Great post.
If i use a matrix with date as columns, it ll show top ToPN of each month. If they are not the same, each month, the table ll show more results than TOPN. Is there a way to resolve that?
Thank you
Hi there
Yes there very well might be a way to do that, do you have a sample dataset that I could look at?
Yes! here is the link: https://drive.google.com/open?id=1oIxVo1sIqFB3kPwUVq7quJk0-r_JhKOF
Thank you
Hi, thank you so much for this post. Thanks to this, I was able to make a similar slicer on my client sales dashboard. The user can see how the metrics on the page change when they select different TopN with the slicer. Awesome!
However, I have one visualization for Location map on my dashboard and I am unsure how to get it to respond to the slicer. It uses the city, state, and country of the ranked clients. It would be great if when the user selects TopN in the slicer, the map would also update and show where those N clients are located. Is this something that can be done?
Hi there
If I understand if your challenge, could you not use the same measure that is used in the Dynamic TopN made easy on your location map visual?
Correct. The sales measure as demonstrated in your example works great, but the map visualization takes location data of City, State, and Country.
Is there a way to create a location measure with City, State, and Country that would likewise be controlled by the same TopN slicer?
Hi. I am using your formula to show in a bar char the top 3 (Group) and also the top 5 (sub-group). Your solution was brilliant, as power bi does not allows more than 1 topN in the chart filters. However, if I apply a slicer, (date column to show one week data, for example) the measure stops working and return all subgroups data.
Could you please let me know why? How to have one or more filters working together with your measure.
Thanks a lot.
Juliana
Thanks for the kind words.
The reason that it will stops working is because it then adds in an additional filter that causes this to happen.
If you can send me a sample dataset and what you are looking to achieve I can then look at how to solve it?
Hi Gibert!
Many thanks for your reply!
See attached the PBix with the group (top 3) and subgroup (top 5) first tab working well, without period filter and second one not working.
Thanks a lot.
Regards,
Juliana
Sorry… couldn’t find where to attach the file.. where can I upload it?
Thanks
Hi there
I will send you an email.
Thanks for the example Gilbert.
I would like to return a Top 20 CAPEX projects based on a slicer. I can work it our for one measure Budget FY20 But i also have another column which is called Spend YTD that id like to add to a slicer so i can either see Top 20 based on Budget FY20 or on spend YTD. Here is my code
Top 20 Major Projects =
VAR RankingContext =
VALUES ( CAPEX[Project])
VAR TopNumber = 20
RETURN
CALCULATE (
[Budget FY20 Spend],
TOPN ( TopNumber, ALL ( CAPEX[Project] ),[Budget FY20 Spend]),
RankingContext
)
Hi dear ,
I’m getting error in <= kindly suggest
Hi there
Could you please send me the DAX where you are getting the error?
Amazing Article !!!
I want to ask if you figured out a way to use legends along with this.
I need to show sales by Supplier (Top N) and use legend for country(or any other field). But the things are breaking and every legend is being shown as an individual row.
Thanks in advance.
Thanks for the kind word.
Currently it is not possible to use conditional formatting for the legend. You could vote for it on https://ideas.powerbi.com
I don’t want any conditional formatting. I need to have a stack-bar chart with suppliers on the axis showing sales. TopN is working fine till here. But as soon as I put country in legends things break up and each legend is shown as a different row.
The same thing happens to me when i try to add another column in the visual table, it breaks. Do you have any updates on your issue?
Hi Alexandra,
Thanks for the comment.
You might have to modify the code to include the additional column as part of the measure?
Thanks so much Gilbert, have not yet managed to make it work in my case, but sure this will bring what I am looking for in my report.
May I ask you to upload the pbix file so we can deconstruct it?
Hi there,
Thanks for the kind words, here is a link to the PBIX
Dynamic TopN made easy with What If Parameter.pbix
I tired your code on a graph , I have a database consisting of schools, the final average grade( for the last exams in the whole year) and an annual average grade.
I wanted to show the topN schools and their annual average grade, with the highest final average grade. However, When I select a number, it returns the final average grade but doesn’t not update the scale of the graph accordingly. How can I fix this, so the graph will show me only the top N selected
Hi there
You would have to apply the same measure and logic in your visual table?
Nothing less than BRILLIANT 🙂
Thanks
Thanks for the kind words!
What ever happened to Erica’s request:
“Erica October 11, 2018 at 5:22 am”
I need this solution as well 🙂
Hi there
The way I would solve it is to create another measure which would be used for the Units, which would still rely on the Slicer for the TopN
The reason it would have to be another measure is because the measure specifically calls out the [Sales Amount] measure.
I would create the measure as shown below
TopN City =
VAR SelectedTop = SELECTEDVALUE('TopN'[TopN])
RETURN
SWITCH(TRUE(),
SelectedTop = 0, [Sales Amount],
RANKX (
ALLSELECTED( 'Orders'[City] ),
[Units]
)
<= SelectedTop, [Units] )
Because this measure uses the same TopN table it will change as the slider changes.
I’ve said it before – and I say it again…….
BRILLIANT 🙂
Thanks for the kind words once again!
Hello Gilbert 🙂
Nice Article. It’s been really helpful. But suppose i want to show the corresponding values such as quantity, number of orders sorted by top revenue values for products, will i need to create separate measures like you’ve shown above?
Hi Shweta
Yes you will have to create the separate measures.
Hello,
this code works perfectly for me if i use only one column, lets say an ID but if i put also another column, ID Names or Region etc it doesn’t work anymore.
How can i modify the code? Thank you!
Hey Alexandra
You have helped me before (THANKS) so I try you again 🙂
How to set a “Parallel Periode” of ‘Is in the last 12 month’ – using the Filter type: ‘Relative Date’ in Power BI?
I link to a data source which give me monthly updated data from SAP.
The problem is, that these data are uploaded every 10th of a giving month, and last month of updated data are either 1 or 2 month older than present month (depending of the present date).
Since I use the ‘Relative Date’ filter function in all my reports, I would like the ability, to set ‘is in the last 12 month’ to a parallel 12 month period based on any giving present date.
I upload a table with only one variable which gives me, the ‘Latest data period’ for the data set I link to – together with my data sets, so that could be part of the solution ?
Example:
Today (May 4th) my dataset has data up till March. So I would like Power BI to filter my data based on the last 12 month up till March, witch mean – April till March – even though the present date is in May.
Hope you are able to help me again 🙂
Thanks
Hi Jesper
What you could do is to filter your date table to only go as far as the data you have in your dataset.
If you then use your relative date slicer from your date table it will only have dates until the end of March, and will show the previous 12 months?
Thank you so much for your help but if I set the relative period till 12 month, it only shows the first 10 month where it has data
Ofcourse I could set the period to 14 month then, but depending of present day of the month, the parallel period changes from 1-2 month
So I would love ti bring in my variable stating last ending period of data, to automaticslky set up my relative period?
I was of the understanding that if the relative date slicer was using a table it would start from the last date in the table and then work backwards from there?
If not another way is to create a column in your date table which references your last date, and then in your column you could use this to then always filter to show the past 12 months of data you have?
Thanks again.
How would you do this ?
The thing about the relative date period only going back till June 2021 (now here in May 2021) could that be because, I call SAMEPERIODLASTYEAR in one of my measurements ?
Sorry – last question 🙂
I am struggling with linking 3 tables together in Power BI
I have Table A which among other data contain ‘EmployeeID’ and ‘As of date’ where ‘As of Date’ is the monthly date for when data was updated. The combination between ‘EmployeeID’ and ‘As of Date’ makes the uniqueness of each row of data.
Then I have a table B, similar to Table A – with the same Date and ID, but with additional I need to include
Last of all, I have my Unique Date table, which is set as ‘Mark as date table’
How do I link these 3 tables together, so I don’t get duplicates of data rows from one table (Table B) ?
I have tried the best I can with setting up the relationships, cardinalities, cross filer directions etc. but it still doesn’t work correctly? What am I doing wrong ?
‘EmploeeID’ and ‘As of date’ between Table A and Table B needs to be One to One, and I then need to link the ‘As of Date’ variable till my date table, for the use of Power BI’s Time Intelligence.
Hope you are able to help me again 🙂
Hi Jesper
What I would do is look to join these tables using Power Query where you can create a composite column (Joining multiple columns together) in each table.
Then Merge the tables with an Inner join and see if that works.
Thank you again
And sorry, but hiw shoukd this columb look like (composite) – can you describe
And how should the innerjoin query look like
Hope you can describe it so I can use it directly
Thanks
I am not sure if it is possible with a composite model, I would suggest building it directly with new datasets to get it working!
Sad
Its external datasets, so I need to find s solution at my site
I have a clustered column chart with line chart where the column series are segmented by the different month each and the column and line values replaced with the Top N category code that you have provided. However, the code does not seem to be working and is not displaying all the months for some category. How can I fix this issue?
Hi Gerald,
It would appear that this is happening because you have got 2 columns that you are looking to use the TOPN on.
Here is an alternative which might work:
Hi, I used your code but had to take out the legend in a stacked Column Chart, is there anyway to use this code and use a legen..
Hi Tony,
I have not had a look as to see if this could work.
My only suggestion is to then not use the stacked column chart because with multiple measures this will create some unknown complexity.