By Kasper de Jonge, original post at PowerPivotblog.nl

I got an excellent question last week on the ask my question 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. )

1. Thanks for this post which is really interesting. I wonder however how you manage the change of year. With this method you have issues on W1 or W52, don’t you? Is something similar possible using TimeIntelligence functions?

2. Michael Shparber says:

Thanks Kasper! I’ve just arrived to the same result using CALCULATE(SUM….,)/Calculate(countrows(summarize(fact,dimdate[week])),….)
Your calculation is much simlier and “straightforward”
Michael Shparber

1. Ben says:

Hi Michael,
Could you please explain how you did it using your date dimension since I’m unable to get the calculation to work as per my comment below?

Thanks,

Ben

3. Cannon Miles says:

Thanks for this post, awesome and simple explanation. But what if you filter down to brand and it does not have sales in one of the weeks? This formula would average the last 3 weeks regardless (which could include a week without sales values), and not the last 3 weeks where sales existed…correct?
Ex: Brand 1
Sales Wk1=80
Sales Wk2=No value
Sales Wk3=90
Sales Wk4=100
This formula of Avg last 3 wks would return 95 instead of 90, I’m very curious how you would adjust the formula to average the last 3 wks with values, that would return 90 in this example?

1. powerpivotpro says:

I revisited this topic this year with a variable denominator:

But that was just intended to account for the “edges” of your calendar table, not periods in which you didn’t sell.

Simple adjustment though, just have the variable calc equal the distinct number of periods with sales rather than the periods found in the calendar table.

4. Ben says:

Could you please explain how to apply this using a date dimension instead of Sales[Week No] from your fact table? When I try with the following it doesn’t work since there are multiple values for CalendarWeek.

test:=IF(countrows(values(DimDate[CalendarWeek]))=1,calculate(averagex(Values(DimDate[CalendarWeek]),[RejectCount]),DimDate[CalendarWeek] VALUES(DimDate[CalendarWeek])-3),blank())

Thanks!

5. Ben says:

Anyone know how to make this work with dynamic date arguments to get a rolling total?
test:=calculate([RejectCount],DATESBETWEEN(DimDate[FullDate],”3/1/2015″,”3/31/2015″))

6. RKTECH says:

How to calculate Average of MTD value only displayed in Pivot table (if its 5th day of the month, I would like to show average of specific to those days)

7. Matt says:

How would you include something so that it does not factor in blank rows. For example Week 1 =\$20, Week 2 = \$30, Week 3 = \$35, Week 4 = Blank (closed and do not want to count in with moving average) Week 5 = \$50