Today I ran across a pretty straightforward DAX challenge that is made so much simpler thanks to variables. So I decided to share it. Remember folks use variables for each and every measure (even if you don’t think you need them). It will prepare you for the toughest challenges :).

In this case we had a pretty straightforward table of survey results where we want to visualize the PCT of Yes answers by weight to a question for each day but also a running AVG of this PCT for the last 3 days.

The data is pretty straightforward:

First I write a measure that calculated the total weight for both Yes and No answers. We then divide the yes answer by the total to get the PCT of Yes answers compared to the total.

Measure =VAR yesq =

CALCULATE ( SUM ( Calc[Weight] ), Calc[Brand A] = “Yes” )

VAR noq =

CALCULATE ( SUM ( Calc[Weight] ), Calc[Brand A] = “No” )

VAR total = yesq + noq

VAR pctYes =

DIVIDE ( yesq, total )

RETURN

pctYes

The benefits of using variables are:

– Performance, each value that is stored in the variable is calculated just once.

– Re-usability, I can reuse the values multiple times

– Ease of use, this code looks pretty straightforward (I like to think)

This now allows us to plot the PCT of yes in a chart:

Finally we are going to use more variables to calculate the avg of this PCT for the last 3 days:

Measure 2 =VAR daterange =

DATESINPERIOD ( Calc[Date], MAX ( Calc[Date] ), -3, DAY )

RETURN

AVERAGEX ( daterange, [Measure] )

Here create a variable to determine the daterange. Using DATESINPERIOD we get the date from the current context (using MAX) to 3 days ago. Then we use the 3 dates in the variable to calculate the PCT for each of those before we finally take the AVG over the results using AVERAGEX.

That’s it, now we can add both the measures in a line chart and call it done.

As Darren points out in the comments below you shouldn’t really be averaging percentages (goes to show you shouldn’t always just do what is asked :)). Again that is also very easy to change our previous measure a bit to facilitate this. Here we use the variable to create a new filter and use the base measure to divide the sum of the yes’s for the 3 days divided by the sum of the total for the 3 days.

Measure 3 =VAR daterange =

DATESINPERIOD ( Calc[Date], MAX ( Calc[Date] ), -3, DAY )

RETURN

CALCULATE ( [Measure], daterange )

That gives us a small variation but probably more correct:

So as you can see using variables you can split up problems into smaller simpler problems. This is especially true when you need to work with intermediate tables like we did with the Dates.

You can download the sample file here.

Hi Kasper,

I have a question regarding the scope of variables:

The declaration of variables can be created recursively so only variables that are needed in the next step are in scope and available. So my question is: does it make any difference (DAX engine efficiently-wise) to rewrite your first measure:

Measure =

VAR yesq =

CALCULATE ( SUM ( Calc[Weight] ), Calc[Brand A] = “Yes” )

VAR noq =

CALCULATE ( SUM ( Calc[Weight] ), Calc[Brand A] = “No” )

VAR total = yesq + noq

VAR pctYes =

DIVIDE ( yesq, total )

RETURN

pctYes

Into this:

Measure =

VAR pctYes =

VAR yesq =

CALCULATE ( SUM ( Calc[Weight] ), Calc[Brand A] = “Yes” )

VAR total =

VAR noq =

CALCULATE ( SUM ( Calc[Weight] ), Calc[Brand A] = “No” )

RETURN

yesq + noq

RETURN

DIVIDE ( yesq, total )

RETURN

pctYes

I don’t think you should be doing an average of percentages, that treats every day as if it has the same weight. I think you should be doing the sum of the yes’s for the 3 days divided by the sum of the total for the 3 days. 🙂

eg.

Measure 2a =

var daterange = DATESINPERIOD(Calc[Date],MAX(Calc[Date]),-3,DAY)

return CALCULATE([Measure],daterange)

good one Darren, this was the requirement so I didn’t think about that.. I have updated the blog post.

Kasper, thanks for a good learning-example.

I’ll also add Yes% D-3 as an explanation before showing your Measure 3.

Yes% D-3 =

var DatesInRange = DATESINPERIOD( Calc[Date]; MAX( Calc[Date] ); -3; DAY )

Return

var YesQ = CALCULATE( SUM(Calc[Weight]); Calc[Brand A] = “Yes”; DatesInRange )

var NoQ = CALCULATE( SUM(Calc[Weight]); Calc[Brand A] = “No” ; DatesInRange )

var TotalQ =YesQ + NoQ

var YesPct = DIVIDE( YesQ; TotalQ )

Return

YesPct

It is a good transition to go from SUMIF(), to SUMIFS(), and then to CALCULATE() above.

I’ll use =AGGREGATE(9;5; D6:D20) and filter the Table to illustrate the different steps,

and explain the similarities with =SUMPRODUCT($D$6:$D$20; –($C$6:$C$20=”Yes”); … )

+ use a PivotTable.

Essentially mix Excel + DAX: 1. SumWeight, 2. Yes%, 3. Yes% D-3, 4. Yes% D-3 (2).

After that l’ll explain that we ca reuse – the already calculate – Yes% ([Measure] in your example).

Yes% D-3 (2) =

var DateInRange = DATESINPERIOD( Calc[Date]; MAX(Calc[Date]); -3; DAY )

Return

CALCULATE( [Yes%]; DateInRange )

I think that should keep us occupied for one hour.

awesome example !

thanks