Do not input private or sensitive data. View Qlik Privacy & Cookie Policy.
Skip to main content
Announcements
Qlik Connect 2025! Join us in Orlando join us for 3 days of immersive learning: REGISTER TODAY

Calculating rolling n-period totals, averages or other aggregations

cancel
Showing results for 
Search instead for 
Did you mean: 
Gysbert_Wassenaar
Partner - Champion III
Partner - Champion III

Calculating rolling n-period totals, averages or other aggregations

Last Update:

Nov 30, 2022 9:45:07 AM

Updated By:

Sonja_Bauernfeind

Created date:

May 4, 2013 3:53:31 PM

Attachments

A question that gets asked regularly is how to calculate a rolling measure over a period of N-months (or weeks or days). For example a 12-month rolling total or a 4-week rolling average. There are several ways to do this. But these approaches have some limitations that need to be kept in mind. I'll try to explain these approaches and some of their limitations.

  • Accumulation
  • RangeXXX functions
  • Set analysis
  • AsOf tables

First let's load some sample data. The SalesData table below will contain sales amount values for twelve consecutive months.

SalesData:
load * inline [
Month, Amount
1,6
2,4
3,7
4,3
5,4
6,9
7,5
8,7
9,8
10,6
11,9
12,7
];

This is a very simple table with little data, but this enough for demonstration purposes.

Once this data is loaded it's possible to create a straight table chart object to display the amount per month and a running total. As expected Month is used as dimension. The expression sum(Amount) will display the amount per month. Now let's add an expression to calculate a running total over three month periods.

This can be done in two ways. The first uses the Accumulation option for expressions. The same expression sum(Amount) is used, but now the Accumulation option is set to 3 Steps Back:

rolling_2.png

The second option uses the rangesum function. That expression looks like this:

rangesum(above(sum(Amount),0,3))

This sums the Amount value on current row and on the previous two rows. The resulting straight table looks like this:

rolling_1.png

This looks good. The rolling 3 months amount is calculated correctly. But what happens if a selection of months is made?

rolling_3.png

The rolling 3 month amount for month 4 is now 3 instead of 14. This is because month 1,2 and 3 are no longer included in the calculation for the rolling 3 month total.

The accumulation option has another issue. It only works when only one dimension is used in the straight table. The rangesum expression can be modified so it can calculate across dimension borders, but the accumulation option can't. The modified rangesum expression adds the total keyword to the above() function:

rangesum(above(total sum(Amount),0,3))

This goes some way to doing what we want, but the issue of displaying the wrong rolling 3 month amount for month 4 isn't solved yet. Contrary to what I first thought there is a solution for this, as Henric pointed out to me in the comments below. By combining the rangesum with the aggr function it's possible to calculate the correct rolling 3 month amounts for each month. The expression needed for that looks like this:

sum(aggr(rangesum(above(total sum({<Month=>}Amount),0,3)),Month))

Read Elif's blog post Accumulative Sums for a more complete explanation.

How about set analysis expressions?

This expression should calculate the sum of amount for the three month period:

sum({<Month={'>=$(=only(Month)-2)<=$(=only(Month))'}>}Amount)


But notice the only() function. This requires that only one month value is selected. After selecting month 4 the result looks like this:

rolling_4.png

This shows the selected month, but also the two previous months. And the values are not accumulated.

Ok, but what about the max function instead of only?

sum({<Month={'>=$(=max(Month)-2)<=$(=max(Month))'}>}Amount)


That gives a different result, but still not what we're looking for:

rolling_7.png

Now only the last three months are shown and again the values are not accumulated.

The 'problem' is that the set is calculated once for the entire chart, not per row. This means that it's not possible here to use Month both as a dimension and in the set modifier in the expression.

There's still an option left to discuss: AsOf tables.

The AsOf table links a period with all the periods in the rolling period. In this example months are used, but it can be applied to any type of period like hours, days or weeks.

For the three month periods needed for a rolling 3 month total this means a month should be linked to itself, the previous month and the month before the previous month. The only exceptions are the first month, which is itself the rolling 3 month period, and the second month that together with the first month is its rolling 3 month period. There are no months before the first month so the first two months cannot run over 3 months.

The AsOf table needed for the rolling 3 month calculations looks like this:

rolling_5.png

This table can be created like this:

AsOfMonth:
load
Month as Month_AsOf,
Month + 1 - IterNo() as Month
Resident SalesData
while IterNo() <= 3;

right join load Month Resident SalesData;

What this does is create three records for every month using the while statement. But that also creates three records for month 1 and 2. This would create a month 0 and a month -1. The right join is used to remove those incorrect month values.

Now that the AsOfMonth table is created the Month_AsOf field can be used instead of the Month field in the straight table. The expression for the straigh table is simply sum(Amount).

rolling_6.png

The straight table now shows the correct rolling 3 month total for month 4.

This can be expanded a little so not only the rolling 3 month can be shown, but also the amount for the month itself. To achieve this the AsOf table is modified by adding a field to label the type of period. And records are added to the table so each Month_AsOf value is linked to the matching Month value:

AsOfMonth:
load 'Current' as Type,
Month as Month_AsOf,
Month as Month
Resident SalesData;

Concatenate (AsOfMonth)
load 'Rolling 3' as Type,
Month as Month_AsOf,
Month + 1 - IterNo() as Month
Resident SalesData
while IterNo() <= 3;

right join load Month Resident SalesData;

There are now two types of periods available: Current and Rolling 3. Additional period types can be added for example for Rolling 6, Rolling 12 month and Year-to-Date periods. You can find examples of these types in the attached AsOf Table Examples.qvw document.

The period type can be used in the chart expressions to calculate the amount for the wanted period:

Current amount: sum({<Type={'Current'}>}Amount)

Rolling 3 month amount: sum({<Type={'Rolling 3'}>}Amount)

Concluding, there are two solutions that do what we want:

1. The rangesum-aggr combination

2. The AsOf table

The first has the advantage that no changes to the data model are needed. It's also possible to dynamically change the period to aggregate over by using a variable instead of a hardcoded number of periods. A disadvantage is that that it's a somewhat complicated expression that also comes with a performance cost.

The AsOf needs changes in the data model to create the AsOf table and fill it with the necessary records. The advantage is that it likely performs better on large data sets. It's also quite versatile since you can add several sets of records to meet different scenario's. The expressions you end up with in the charts are also less complicated. That said, it will likely take you some time to fully understand the AsOf table concept and realize all the places where you can put it to good use.

In the end you'll have to decide for yourself which solution is appropriate in your situation. With regards to the performance of one or the other solution, you will simply have to test to discover if the performance is acceptable. But of course such testing is already part of your development process, right?

I'd like to thank John Witherspoon for introducing me to the AsOf tables concept and Henric for pointing out the solution using the rangesum function in combination with the aggr function.

Tags (2)
Labels (2)
Comments
abhaysingh
Specialist II
Specialist II

((sum({<  [Txn Type] = {'AGEING'},  [Calendar Month] = {'Apr'}>}Aggr(DISTINCT [Net Outstanding Amount],[Invoice Number]))

/ (sum({<[Txn Type] = {'INVOICE'}, [Calendar Month] = {'Mar'}>} [Invoice Amount])

+ sum({<[Txn Type] = {'INVOICE'}, [Calendar Month] = {'Feb'}>} [Invoice Amount])

+sum({<[Txn Type] = {'INVOICE'}, [Calendar Month] = {'Jan'}>} [Invoice Amount]) )/3  ) *30))

this is my dso exp. can any one please suggest the exact expression?

month wise dso trend should be plot for 3 month.. and for apr above calculation is true but for for feb 3 month average of invoice amount should take month jan2015,dec2014,nov2014..and same pattern for jan 2015

Anonymous
Not applicable

Thanks Gysbert! The "AsOf" table is definitely the best solution to work with this kind of data analysis. I have to question that I hope you be able to provide an example. The Fist Question is What Happen if I Need to represent the period in this way "Mar-2010" or use the simple month for example "Mar", I use your example and I put that in this way, but I don't now if this is the right solution.

LOAD

Period as AsOfPeriod

,'YTD' as PeriodType

,date(addmonths(Period,1-iterno()),'YYYYMM') as Period1

,Year(Period) as Year,

Month(Period) as Month,

Month (Period) & ' - ' & Year(Period) as Period2,

'Q' & Ceil([Month (#)] / 3) as Quarter

RESIDENT PeriodTbl

WHILE iterno() <= num(month(Period));


The second question is can you provide an example of QTD (Quarter to Date).


Best Regard!! and thanks again for your post

Gysbert_Wassenaar
Partner - Champion III
Partner - Champion III

If you want to change the format of the months then simply change the format string parameter in the date() function:


,date(addmonths(Period,1-iterno()),'MMM-YYY') as Period1


To generate QTD records for the AsOf table you can use something like this:


CONCATENATE (AsOfPeriodTable)

//Load QTD into AsOf table

LOAD

Period as AsOfPeriod

,'QTD' as PeriodType

,date(addmonths(Period,1-iterno()),'YYYYMM') as Period1

,Year(Period) as Year

RESIDENT PeriodTbl

WHILE iterno() <= mod(month(Period)-1,3)+1;

Not applicable

I want to create the rolling 3 months in back end. and directly one feild should be of  this rolling month.  can u please explain how it will get

Not applicable

Hi Gyssbert,

I'm using this approach to see differents metric over periods, but I have a trouble when I try to create a Growth Graphic. I need to compare Current Year vs Previos Year, any idea to do this? also over periods..

Thanks for your help

Best Regards!!!

Gysbert_Wassenaar
Partner - Champion III
Partner - Champion III

If your question is not about the document I wrote then please post your questions in a discussion of its own.

Not applicable

It is a good point, but I think that the best solution is pre-calculate the rolling average in the script.

You can find below my solution:

for i = 1 to 3

  Join(RAW_DATA)

  LOAD

  ID,

  Date(Floor(Date + 7*$(i)), 'DD/MM/YYYY') as Date,

  Data as Data_PW$(i),

  Resident RAW_DATA;

NEXT

FINAL_DATA:

LOAD

  ID,

  Date,

  Data,

  RangeAvg(Data, Data_PW1, Data_PW2, Data_PW3) as RollingAverage,

Resident

  RAW_DATA

;

DROP Table RAW_DATA;

This is for a 4 week rolling average, but you can change the number of iterations in the the loop and the RangeAvg() function.

If you need a month rolling average you must use the AddMonths() function.

If you need an accumulate, you must change the RangeAvg() function by the RangeSum() function.

I hope it would be useful!!!!

Gysbert_Wassenaar
Partner - Champion III
Partner - Champion III

Great, but now consider the case where you need to calculate this using selections in up to twelve dimensions and also not just a rolling 4 week, but also YTD, QTD, MTD, Same Month Last Year, etc. Still think this is best done in the script?

Not applicable

Hi,

You can calculate the rolling/YTD/QTD/etc. for all dimensions you need. That is not a problem.

But I think that it is the best way because you cannot use set analysis in a chart with time dimension. I mean, you cannot use in a set analysis the dimension that you use in the chart.

For me is better calculate all in the script. Because if you have lot of data set analysis could produce a bad performance.

Kind regards,

Mario

Gysbert_Wassenaar
Partner - Champion III
Partner - Champion III
You can calculate the rolling/YTD/QTD/etc. for all dimensions you need. That is not a problem.

Really? Do you know that the factorial of 12 is? Do you really want to create that many precalculated result tables? That's what you will need if you want to deal with all the possible combinations of selections in twelve dimensions.

Version history
Last update:
‎2022-11-30 09:45 AM
Updated by: