Is Take() the best way to calculate a moving sum?

JenniferMurphy

Well-known Member
Joined
Jul 23, 2011
Messages
2,691
Office Version
  1. 365
Platform
  1. Windows
About a week ago, Cubist introduced me to the Take function. I have made great use of it.

I just had a situation where I needed to calculate a moving average. The Take function seems like the perfect solution, but I would appreciate any comments.

Here are several examples. The mini-sheet is below.

1712368755353.png
1712368791008.png
1712368855608.png
1712369024144.png


Notice that I can't easily get the moving average from the moving sum without calculating the number of values in the sum, something that Take does for me.

Take Function.xlsx
CDE
5N=5
6ValueSum of Last NAverage of Last N
7333.00
8694.50
91103.33
107174.25
113204.00
121183.60
132142.80
145183.60
153142.80
168193.80
Moving Sum
Cell Formulas
RangeFormula
D7:D16D7=SUM(TAKE(OFFSET(Table2[[#Headers],[Value]],1,0):[@Value],-Num))
E7:E16E7=AVERAGE(TAKE(OFFSET(Table2[[#Headers],[Value]],1,0):[@Value],-Num))
Named Ranges
NameRefers ToCells
'Moving Sum'!Num='Moving Sum'!$C$5D7:E16
 

Excel Facts

Can you AutoAverage in Excel?
There is a drop-down next to the AutoSum symbol. Open the drop-down to choose AVERAGE, COUNT, MAX, or MIN
I still think my preference is a LAMBDA...the pink cells use the MAP helper LAMBDA function, and in it, the row indexes are created using the SEQUENCE function. We step along each row index (called r inside the LAMBDA function...an arbitrary designation)...and use the same DROP/TAKE method for efficiently selecting the desired portion of the data range. This is a variation on the earlier LAMBDA approach...a bit shorter and still only one formula to spill the results.
One caveat with the Lambda solution is that it cannot be inside the table. When I tried that, I got a #SPILL error on every row. Is that correct, or did I do something wrong?

I wondered how you managed to have all the same headers. Then I realized that those columns were outside the table.
 
Upvote 0
Jennifer, I wanted to follow up with a couple of things, and offer a nuanced and expanded explanation to Rory's comment. First, the "ridx" that appears in some of the options I posted previously is a named variable used within the LET function. LET allows us to define a variable name followed by the expression for that variable. We might choose to establish this named variable for the same reason we might use Excel's Name Manager for establishing a named variable: for convenience in those cases where a value is used subsequently, perhaps multiple times, and we don't want to repeat the cumbersome expression for it. Another reason for doing so is to compute intermediate results that are needed for a final result, and rather than placing those intermediate results in helper cells on the worksheet, we can assign them to the named variables for later use in the LET function...so LET can essentially be used to wrap a series of computations into a single expression, and only the last expression in LET will be reported out as the result.

In the expressions where I defined "ridx", ridx represents the row index for whichever Value in the table happens to be the targeted one. For example, 5 appears in the 8th row index position of the [Value] column. That happens to be found on row 14 of my worksheet...and the #Headers for Table2 happen to be located on row 6 of my worksheet. Therefore, when the formula for computing the "Sum of Last N" is pulled down to this particular row (14), the ridx expression, which is ROW()-ROW(Table2[#Headers]), will be computed as 14 - 6, which is 8...meaning the value of 5 is found in the 8th row of the Value column. This is used to determine whether the arrays to be summed or averaged should use the last "N" values (when ridx >= N) or the last "ridx" values (when ridx < N).

The LAMBDA-based formulas I offered assumed the results were to be delivered somewhere outside the table, and the use of SEQUENCE inside the MAP LAMBDA helper function will cause all of the results to "spill" from a single formula (i.e., the MAP formula is entered in one cell only, and the Sum of Last N for all values in the [Value] column of the table will be delivered as a spilling array). To Rory's point, this works outside of the table, but not inside the table, because official Excel tables have a limitation in that spilling results are not accommodated. This behavior is almost by necessity, because a formula entered into the top cell of a table is automatically applied to all cells below it, and this would conflict with spilling results. You can see this problem appearing as a series of #SPILL! errors in the [Sum of Last N_a] column, which I suspect is similar to what you observed. But if we understand the behavior, we can still see these results: consider what happens when we wrap the formula with a TEXTJOIN function to cause all of the spilling results to be combined into a single cell (see the [Sum of Last N_b] column). Then we'll see that the single formula initially entered into the topmost cell is delivering all desired results, but Excel's "table" behavior causes this same formula to be repeated for every cell in the column. Importantly, this illustrates that LAMBDA functions can be used inside official tables, but they need to be used in such a manner to deliver only a single result, and not to "spill" multiple results.

In this case, as I mentioned above, it is the SEQUENCE function that causes multiple results to be spilled by the LAMBDA function. We can revise the Sum and Average formulas to use the row index for each value (rather than an array of row indexes), knowing that the row index will be computed as the formula is automatically copied down the column due to Excel's table behavior. In these revised formulas (columns D and E), I've used the same expression for "ridx" as used in other, earlier formula variants. But it's really not necessary to go to this trouble inside the table...in fact, it is rather silly to pass a single value to the MAP function (rather than an array). If we're going to rely on Excel's official table formula-copying-down behavior to automatically generate the correct ridx value, we can use the non-LAMBDA versions of the formulas inside the table, as shown in columns H and I. Bottom line: if you want the results inside the table, I would take an approach like that shown in cols H and I and rely on the table behavior to deliver all of the results.

Note that two of the formula refer the the "Num" named variable. You don't really need to do that if you would prefer to simply refer to the fixed cell $C$5, which contains the value of "N".
Cell Formulas
RangeFormula
D7:D16D7=MAP(ROW()-ROW(Table2[#Headers]),LAMBDA(r,SUM(DROP(TAKE([Value],r),MAX(0,r-$C$5)))))
E7:E16E7=MAP(ROW()-ROW(Table2[#Headers]),LAMBDA(r,AVERAGE(DROP(TAKE([Value],r),MAX(0,r-$C$5)))))
F7:F16F7=MAP(SEQUENCE(ROWS(Table2)),LAMBDA(r,SUM(DROP(TAKE([Value],r),MAX(0,r-Num)))))
G7:G16G7=TEXTJOIN(", ",,MAP(SEQUENCE(ROWS(Table2)),LAMBDA(r,SUM(DROP(TAKE([Value],r),MAX(0,r-Num))))))
H7:H16H7=LET(ridx,ROW()-ROW(Table2[#Headers]),SUM(DROP(TAKE([Value],ridx),MAX(0,ridx-$C$5))))
I7:I16I7=LET(ridx,ROW()-ROW(Table2[#Headers]),AVERAGE(DROP(TAKE([Value],ridx),MAX(0,ridx-$C$5))))
Named Ranges
NameRefers ToCells
Num=Sheet2!$C$5D7:I16
 
Upvote 0
Jennifer, I wanted to follow up with a couple of things, and offer a nuanced and expanded explanation to Rory's comment.

. . .

In the expressions where I defined "ridx", ridx represents the row index for whichever Value in the table happens to be the targeted one.

Aha! ridx = row index. I did understand that it was the Let variable, I just wondered what it stood for.

I'll have to study the Lambda stuff later.

Thanks for all of the information.
 
Upvote 0

Forum statistics

Threads
1,224,813
Messages
6,181,109
Members
453,021
Latest member
Justyna P

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top