Excel Dynamic Array formula to create a running product of a column
Asked Answered
A

3

3

I need to create a running product from a column of numbers (I could use a row, but a column is easier to demonstrate here.) The input might be any arbitrary array. In fact, in the application where I would deploy this, it will not be a range, but rather another dynamic array within a LAMBDA formula. Here is an example of the Input column of numbers and the desired Output from the formula:

Inputs Expected Dynamic Array Output
10 10
8 80
3 240
4 960
5 4800

The formula would spill the results.

There are lots of solutions for a running total, but I've found no solution for a running product. I have tried a few different approaches, including SUBTOTAL and AGGREGATE with no success. I have also built a number of approaches that get the result, but are hard-coded to a fixed number of rows. I need the formula to adapt to any arbitrarily sized number of rows. The following formula is the closest I have gotten so far.

This LET formula delivers the result, but, as you can see is fixed to 5 rows:

=LET( a, {10;8;3;4;5},
       v, SEQUENCE( ROWS(a) ), h, TRANSPOSE( v ),
       stagr, (v - h + 1) * (v >= h),
       m, IFERROR(INDEX( a, IF(stagr>0,stagr,-1), ), 1),
       almost, INDEX(m,v,h) * INDEX(m,v,h+1) * INDEX(m,v,h+2) * INDEX(m,v,h+3) * INDEX(m,v,h+4),
       result, INDEX( almost, , 1 ),
       result )

The arbitrary array of numbers input is placed in the variable a.

The next step is to create some indexes that will be used to address these numbers: v is a sequence of vertical rows for each number in a and h is a the same sequence, but transposed into columns. stagr is an index matrix that is created from v and h that will later be used to address each item in a to form it into a multiplication matrix. If you replace the last result with stagr, you can see the shape of stagr. It just shifts a column down by one row until they are shifted all the way down.

stagr

Now we create the mulitplication matrix m using stagr by simply using INDEX, like this: INDEX(a,stagr). But this is not exactly what is needed because it takes the first row value (10) and replicates it because an INDEX of 0 is treated the same as 1. To get what we want, I forced an error by using and internal IF statement like this: INDEX( a, IF(stagr>0,stagr,-1) ) to replace the 0 results with -1. i.e. it will produce this:

m with errors

Now, replace the errors with 1's by using IFERROR, so this explains how m is created and why. The result is a matrix like this:

multiplication matrix

and by multiplying m row-wise, we get the output we want, but this is where I fail.

For illustration, I created a variable almost that shows how I am trying to do a row-wise multiplication.

almost, INDEX(m,v,h) * INDEX(m,v,h+1) * INDEX(m,v,h+2) * INDEX(m,v,h+3) * INDEX(m,v,h+4)

You can see that I crudely multiplied one column times the next and the next... and using h + offset to get there. This produces the almost matrix and result just delivers the first column of that matrix, which contains the answer.

While an answer might be a good replacement for almost that would be dynamically sized, that is not my real question. I want a running product and I suspect that there is a wholly different approach than simply replacing my almost.

Just to be clear, the result must be a dynamic array that spills with no helper cells or CSE drag-down.

oh... and no VBA. (@stackoverflow - please add a no-VBA tag)

Austenite answered 15/7, 2021 at 19:46 Comment(8)
You will need another LAMBDA to do this. All formula that would be multiplicative currently accept arrays and return a single value. As stated, it would be easy if it where simply summation. I have been wrong before and will gladly accept that I am wrong here just to learn something new, but I am not seeing a way to do this except possibly LAMBDA.Cuprous
Cheers @ScottCraner - I am not yet able to use LAMBDA function, so I will probably have to wait. I think I can see what you are saying, but honestly, even with that advice, I am struggling to visualize how that would work. I have some other plans for LAMBDA that involve recursion and iteration, so let's see... Thanks for the fast answer.Austenite
Maybe, Input : A2:A6, Output : B2, formula copied down : =IF(N(B1),A2*B1,A2)Jorgan
Thanks @bosco_yip, but it must be a dynamic array that spills - no drag-down formulas.Austenite
Or, in B2 enter : =SUBTOTAL(106,OFFSET(A$2,,,ROW($1:$7),)) >> Select B2:B6 >> Ctrl+Shift+EnterJorgan
That's an interesting solution, but requires a specific range as input whereas I need a dynamic array as input that can be a range, but is not required to be a range. Note that a is {10;8;3;4;5} which is a dynamic array, not a range. If there were a way to replicate the OFFSET with an INDEX, your proposal would solve the gordian knot without all of the gymnastics that my LET is doing.Austenite
Or another one, B2, enter : =IFERROR(SUBTOTAL(106,OFFSET(A$2,,,ROW(INDIRECT("1:"&COUNT(A:A))),)),"") >> B2:B10 ( or more than B10 that allow dynamic range in growing) >> Ctrl+Shift+EnterJorgan
Let us continue this discussion in chat.Jorgan
G
3

Whilst not available to everybody (yet) we can use SCAN()

enter image description here

Formula in A1:

=SCAN(1,{10,8,3,4,5},LAMBDA(a,b,a*b))
  • The 1st parameter is our starting value, meaning the 1st calculation in the nested LAMBDA() is '1*10'.
  • The 2nd parameter can both take a 1D- & 2D-array (written or range-reference).
  • The 3rd parameter is a nested LAMBDA() where the result of our recursive function will then be used for the 2nd calculation; '10*8'. And the 3rd...etc. etc.
  • In the above sample a vertical array is spilled but when horizontal input is used this will obviously result in an horizontal spilled output. When a 2D-array is used this will spill a 2D-array as result.
Genuine answered 1/10, 2021 at 9:59 Comment(5)
That's it! I had just commented to Scott Craner that I saw BYCOL and thought it would be the pathway to a solution, but SCAN gets straight to it. I don't yet have access to SCAN (or BYROW, BYCOL, LAMBDA...), but as soon as I do, it will open up a lot of possibilities. Nice work and many thanks!Austenite
I had first put down a very roundabout way, you can check that with previous versions (if you are able to see those) using MAKEARRAY() and BYROW(), but indeed, SCAN() get's straight to the point. @markfitzpatrick. It was a nice question I had worked on for a bit here =)Genuine
Thanks JvdV - It is a pattern that has shown up a few times in OP's. The first time I saw it was for creating (all combinations of 5 columns of symbols)[#67905893. Scott's DPRODUCT is an out-of-the-box approach that is pure black magic, but it requires a range and therefore will not fit the question. Bosco Yip also made a great proposal, but range based. Yours does it concisely - I can't wait to put my hands on the new functions.Austenite
@markfitzpatrick, tons of new possibilities. I too am curious what is next! Glad I can allready play around with these new functions to try and understand them.Genuine
Hey - have a look at the answer from Alister down below. Clever solution for those without access to LAMBDA.Austenite
C
4

The only way I can find is to use DPRODUCT with OFFSET, but that requires a title row. It does not matter what is in the title row(it can even be empty), just that it is included.

=DPRODUCT(OFFSET(A1,0,0,SEQUENCE(COUNT(A:A),,2)),1,$ZZ1:$ZZ2)

The $ZZ1:$ZZ2 can be any empty cell reference.

enter image description here

If the values in A are dynamic then we can do:

=DPRODUCT(OFFSET(A1,0,0,SEQUENCE(ROWS(A2#),,2)),1,$ZZ:$ZZ)

enter image description here

Cuprous answered 30/7, 2021 at 18:56 Comment(4)
I haven't thought about DPRODUCT in years. This is really out of box thinking. I tried it out before my wife reminded me of other obligations and it's bedtime here. Nonetheless, I woke up and gave it another go. When I am back up, I will try to incorporate it. Thanks, Scott.Austenite
Black magic: it works and I cannot see how you got there. @Jorgan proposed =SUBTOTAL(106,OFFSET(A$2,,,ROW($1:$10),)) which also produces the result. These cover the case for ranges, but the input is not required to be a range. Your initial assessment about this being solved in a recursive LAMBDA is probably the way forward. One illustrative use-case is to generate a repetition array for a symbol set which is solved in the same way as here.Austenite
Hey Scott - I just saw the BYCOL function. I think when that is released, it will allow almost to be dynamic and there will be a running product solution for dynamic arrays. Can't wait to see it released...Austenite
Hey - have a look at the answer from Alister down below. Clever solution for those without access to LAMBDA.Austenite
S
4

There are plenty of interesting answers here. But, if summation is easy why not take logarithms of the number you want to multiply, sum those logarithms and then calculate the exponent of your sum to return to the product of the original numbers.

i.e. exploit the fact that ln(a * b) = ln(a) + ln(b)

Sounder answered 5/5, 2022 at 21:24 Comment(2)
That is a very clever solution! For those without access to LAMBDA, it solves the problem. Nice work! Welcome to Stackoverflow.Austenite
Great idea @Alister, the only limitation is that the input data must be only positive real numbers.Convector
G
3

Whilst not available to everybody (yet) we can use SCAN()

enter image description here

Formula in A1:

=SCAN(1,{10,8,3,4,5},LAMBDA(a,b,a*b))
  • The 1st parameter is our starting value, meaning the 1st calculation in the nested LAMBDA() is '1*10'.
  • The 2nd parameter can both take a 1D- & 2D-array (written or range-reference).
  • The 3rd parameter is a nested LAMBDA() where the result of our recursive function will then be used for the 2nd calculation; '10*8'. And the 3rd...etc. etc.
  • In the above sample a vertical array is spilled but when horizontal input is used this will obviously result in an horizontal spilled output. When a 2D-array is used this will spill a 2D-array as result.
Genuine answered 1/10, 2021 at 9:59 Comment(5)
That's it! I had just commented to Scott Craner that I saw BYCOL and thought it would be the pathway to a solution, but SCAN gets straight to it. I don't yet have access to SCAN (or BYROW, BYCOL, LAMBDA...), but as soon as I do, it will open up a lot of possibilities. Nice work and many thanks!Austenite
I had first put down a very roundabout way, you can check that with previous versions (if you are able to see those) using MAKEARRAY() and BYROW(), but indeed, SCAN() get's straight to the point. @markfitzpatrick. It was a nice question I had worked on for a bit here =)Genuine
Thanks JvdV - It is a pattern that has shown up a few times in OP's. The first time I saw it was for creating (all combinations of 5 columns of symbols)[#67905893. Scott's DPRODUCT is an out-of-the-box approach that is pure black magic, but it requires a range and therefore will not fit the question. Bosco Yip also made a great proposal, but range based. Yours does it concisely - I can't wait to put my hands on the new functions.Austenite
@markfitzpatrick, tons of new possibilities. I too am curious what is next! Glad I can allready play around with these new functions to try and understand them.Genuine
Hey - have a look at the answer from Alister down below. Clever solution for those without access to LAMBDA.Austenite

© 2022 - 2025 — McMap. All rights reserved.