Advanced DAX calculation: doing a moving grouped average in PowerPivot

I got an excellent question last week on the ask my answers page that brought me new understanding of DAX. So finally a new interesting (I hope) blog post on DAX.

Let’s say I have a set of sales per week of a specific brand:

Now I want to have the average of sales per week (slicable by brand) and compare it to the average of the last 3 weekly totals. I prepare  the pivottable :

Now for the moving average of the last 3 weeks we are going to do some interesting DAX. First of all we want to use the current “Week No” as a base value, we need to check if our formula has one week in the current row context, otherwise we cannot get a moving average over a specific week. We check the number of values in the Sales[Week No] column using:

if(countrows(values(Sales[Week No])) = 1

Next we want to get the values of sales from this week and the previous two week to get a grouped average from. We can get a grouped average on Sales[Week No] of the Sales using the AVERAGEX function:

AVERAGEX(VALUES(Sales[Week No]), Sales[Sum of Sales])

Notice we use VALUES(Sales[Week No]) in function to group on the distinct values of Sales[Week No]. This function on its own will only return the average of the current Sales[Week No], we need to override the filter context to return the last current and previous two week. To override the current context with a broader set of values we can use the Calculate function.

This Calculate function will return a table of the current week plus the previous two:

CALCULATE(<grouped AVG function> , Sales[Week No] <= VALUES(Sales[Week No]) &&  Sales[Week No] > VALUES(Sales[Week No])-3)

If you combine the two we get the overall DAX function:

=if(countrows(values(Sales[Week No])) = 1,
	CALCULATE(
		 AVERAGEX(VALUES(Sales[Week No]), Sales[Sum of Sales])
		 ,Sales[Week No] <= VALUES(Sales[Week No]) &&  Sales[Week No] > VALUES(Sales[Week No])-3
		)
, blank())

This will give the following Pivottable:

The most interesting thing to notice here is that VALUES(Sales[Week No]) contains two different values in a single function. Let’s take a look at the DAX function again:

=if(countrows(values(Sales[Week No])) = 1,
	CALCULATE(
		 AVERAGEX(VALUES(Sales[Week No]), Sales[Sum of Sales])
		 ,Sales[Week No] <= VALUES(Sales[Week No]) &&  Sales[Week No] > VALUES(Sales[Week No])-3
		)
, blank())

The red function will return the actual row and filter context that we are in. To see what this row and filter context is all about check this link. The calculate function will change the context of the first argument, in this case we use AVERAGEX in the first argument to group on the distinct Sales[Week No] of the changed context. The blue function returns 3 Sales[Week No] to do the Average over.

It can look confusing but again shows the amazing capabilities of the CALCULATE and AVERAGEX functions. It took me some time to come up with CALCULATE in this particular scenario (thanks to my colleague Jeffrey who also made a in depth blog post about this subject. )

  • Reuvain

    Why are you using AVERAGEX instead of the regular AVERAGE function?

  • Reuvain

    Are you trying to do an average of weekly totals?
    If so, I would reword this line
    “Now I want to have the average of sales per week (slicable by brand) and compare it to the average of the last 3 weeks. I prepare the pivottable :”
    to
    “Now I want to have the sum total of sales per week (slicable by brand) and compare it to the average of the last 3 weekly totals. I prepare the pivottable :”

  • Kasper de Jonge

    Hi @Reuvain , Yes I am grouping the values by
    Sales[Week No] before doing an average.