The reason I'm doing this is so I don't have to use matrix multiplication and can use sumproduct instead. I can do it with matrix multiplication, but I'd rather not have the overhead of explaining what that is and how to do it to my colleagues. I need to apply a scalar to two arrays that I'm applying a sumproduct against. The problem is that since the scalar is length 1, it breaks the sumproduct formula.
Here is what the formula looks like:
=sumproduct(1/sumif(conditionals), array 2, array 3)
What I would like to do is scale the first array, which is my scalar, based on the size of the other 2 arrays. I have tried this, which I figured wouldn't work, but it should get the idea of what I'm trying to accomplish across:
=sumproduct(rept(1/sumif(conditionals),count(array 2)), array 2, array 3)
The problem here is that the rept function returns a string, which I'm unable to use round() or any other formulas I can think of to make into an 1xN array of that scalar. I should note I'm not trying to cast the variable necessarily, but if there is a way to do that it would likely solve the problem.
Any ideas?
Have you tried?
=sumproduct((1/sumif(conditionals))*'array 2'*'array 3')
In addition, wouldn't that be mathematically the same as
=(1/sumif(conditionals))*sumproduct('array 2','array 3')
The following formula should solve your problem:
=SUMPRODUCT(
ROW(INDIRECT("1:" & COUNT(array 2))) ^ 0 / SUMIF(conditionals),
array 2,
array 3
)
This works as follows:
First obtain the number of elements of array 2:
COUNT(array 2)
Then convert it into a 'virtual' range reference (only rows):
INDIRECT("1:" & COUNT(array 2))
Then determine the row numbers from this virtual range reference, the result of which is {1,2,...,COUNT}:
ROW(INDIRECT("1:" & COUNT(array 2)))
Finally, convert the row number by raising to the power 0:
ROW(INDIRECT("1:" & COUNT(array 2))) ^ 0
This gives you the desired array {1,1,...,1} with COUNT elements, which can be used in the SUMPRODUCT() function.
Related
I am a bit lost with Excel. I have two values
Length
Weigth
I am looking for a solution for the problem below:
Take length and weight from the input values, find the next upper value for both in the matrix and receive the value from the value column. And if there is no upper value for my input, i want to have a fallback and always use teh value 1.0.
How to solve this?
I have a matrix which has these values inside:
You could try:
=IFERROR(INDEX(SORT(SORT(FILTER(MATRIX, (LENGTHS>length)*(WEIGHTS>weight)),1,1),2,1),1,3),1)
, where MATRIX, LENGTHS, and WEIGHTS refer to the data on the right, whilst length and weight refer to the cells containing 80 and 450.
Just seen you said H6 was what I referred to as length - if so:
=IFERROR(INDEX(SORT(SORT(FILTER(K6:M11, (K6:K11>H6)*(L6:L11>H7)),1,1),2,1),1,3),1)
in H8.
You can use XMATCH to get the next value as follow:
=LET(f, FILTER(D2:F7, D2:D7>B1),
IFNA(INDEX(INDEX(f,,3), XMATCH(B2, INDEX(f,,2),1)), 1))
If you want to treat the error when the length condition is not satisfied, you can modify it as follows:
=LET(f, FILTER(D2:F7, D2:D7>B1, ""), IF(#f="", "No match for length condition",
IFNA(INDEX(INDEX(f,,3),XMATCH(B2, INDEX(f,,2),1)), 1)))
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.
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:
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:
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)
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.
If the values in A are dynamic then we can do:
=DPRODUCT(OFFSET(A1,0,0,SEQUENCE(ROWS(A2#),,2)),1,$ZZ:$ZZ)
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)
Whilst not available to everybody (yet) we can use SCAN()
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.
So there are a bunch of ways to reverse a list to turn it into a new list, using helper columns. I've written some code that does use helper columns to reverse a list and then use it.
I'm wondering how I would reverse a list without using a helper column for use in a sumproduct - for example,
=sumproduct(Reverse(A1:A3),B1:B3)
This array formula will reverse the order of a vertical array:
= INDEX(B18:B21,N(IF({1},MAX(ROW(B18:B21))-ROW(B18:B21)+1)))
Also, this reverses a horizontal array:
= INDEX(A1:D1,N(IF({1},MAX(COLUMN(A1:D1))-COLUMN(A1:D1)+1)))
EDIT
More generally, to vertically flip a matrix instead of just an array (which is just a one-dimensional matrix), use this array formula: (e.g. for range A1:D2)
= INDEX(A1:D2,N(IF({1},MAX(ROW(A1:D2))-ROW(A1:D2)+1)),
N(IF({1},COLUMN(A1:D2)-MIN(COLUMN(A1:D2))+1)))
And to horizontally flip a matrix, use this:
= INDEX(A1:D2,N(IF({1},ROW(A1:D2)-MIN(ROW(A1:D2))+1)),
N(IF({1},MAX(COLUMN(A1:D2))-COLUMN(A1:D2)+1)))
And a bonus... to flip a matrix horizontally and vertically in one shot (i.e. rotate it 180 degrees):
= INDEX(A1:D2,N(IF({1},MAX(ROW(A1:D2))-ROW(A1:D2)+1)),
N(IF({1},MAX(COLUMN(A1:D2))-COLUMN(A1:D2)+1)))
Actually this last one here could more generally be used to flip either a horizontal or vertical array.
This will do what you are asking:
=SUMPRODUCT(INDEX(A:A,N(IF(1,{3;2;1}))),B1:B3)
To make a little more dynamic you can use this array formula:
=SUM(INDEX(A:A,N(IF(1,LARGE(ROW(A1:A3),ROW(A1:A3)))))*B1:B3)
Being an array formula, it needs to be confirmed with Ctrl-Shift-Enter, instead of Enter when exiting Edit mode.
Found an easy solution that works in the latest Excel versions:
=SORTBY(*rowarray*, column(*rowarray*),-1)
=SORTBY(*columnarray*, row(*columnarray*),-1)
For what it's worth, here's another completely different method to reverse an array. (I'm posting this as a separate answer just because it is apples and oranges to the other answer I already provided.)
Instead of reversing the order of the array by reversing the indexing, it is also possible to use matrix multiplication (MMULT) to accomplish this.
If your data in A1:A3 is {1;3;5} (for example) then the following matrix multiplication effectively reverses this array:
[0 0 1] [1] [5]
[0 1 0] * [3] = [3]
[1 0 0] [5] [1]
In order to generate that matrix of 1's and 0's above, you can do this (line break added for readability):
= (ROW(INDEX(A:A,1):INDEX(A:A,ROWS(A1:A3)))=
(COLUMN(INDEX(1:1,ROWS(A1:A3)))-COLUMN(INDEX(1:1,1):INDEX(1:1,ROWS(A1:A3)))+1))+0
So in the end, the formula to reverse this array would be:
= MMULT((ROW(INDEX(A:A,1):INDEX(A:A,ROWS(A1:A3)))=
(COLUMN(INDEX(1:1,ROWS(A1:A3)))-COLUMN(INDEX(1:1,1):INDEX(1:1,ROWS(A1:A3)))+1))+0,A1:A3)
This same line of thinking can be used to reverse a horizontal array. For example if A1:C1 is {1,3,5}, then:
[0 0 1]
[1 3 5] * [0 1 0] = [5 3 1]
[1 0 0]
Note how the matrix of 1's and 0's is the second argument this time instead of the first argument.
Using the same general line of reasoning, you can get to this formula to reverse a horizontal array.
= MMULT(A1:C1,(ROW(INDEX(A:A,1):INDEX(A:A,COLUMNS(A1:C1)))=
(COLUMN(INDEX(1:1,COLUMNS(A1:C1)))-COLUMN(INDEX(1:1,1):INDEX(1:1,COLUMNS(A1:C1)))+1))+0)
This method has two major disadvantages as compared two the N(IF(...)) solution, namely:
It's way longer.
It only works for numbers since MMULT requires numbers, but the other method works if the cells contain anything (e.g. text).
I was using this solution to reverse arrays without helper columns until just recently when I learned about the N(IF(...)) alternative.
Actually you can make the formula in your Question work (with a small UDF()):
Pick a cell and enter:
=SUMPRODUCT(reverse(A1:A3),B1:B3)
with this in a standard module:
Public Function reverse(rng As Range)
Dim ary(), N As Long, i As Long
N = rng.Count
ReDim ary(1 To N)
i = N
For Each r In rng
ary(i) = r.Value
i = i - 1
Next r
With Application.WorksheetFunction
reverse = .Transpose(ary)
End With
End Function
With Dynamic Arrays
This is the best solution I have found.
=SORTBY(list,SEQUENCE(ROWS(list),1,ROWS(list),-1))
https://exceljet.net/formula/reverse-a-list-or-range
=INDIRECT(ADDRESS(ROW()+COUNTA(A1:$A$3)+1-ROW(A1),1))
I have this excel formula that someone has created without documentation and I'm struggling to understand it.
=SUMPRODUCT(-MID(TEXT(MID(TEXT(F2,REPT(0,15)),ROW(INDIRECT("1:15")),1)*{2;1;2;1;2;1;2;1;2;1;2;1;2;1;2},"00"),{1,2},1))
it looks like it creates a "random" digit from another number.
There are few key things I'm stuggling with:
* why is there an array ({1,2}) given to a MID() function?
* since there is a SUMPRODUCT(), which needs an array, I'm assuming that the result of the -MID() function is some sort of an array, how do I see what it is?
* what does multplying by an array {2;1;2;1;2;1;2;1;2;1;2;1;2;1;2} does?
* the INDIRECT() functions seems to always return 1?
any help would be appriciated.
There is a function in EXCEL called Evaluate Formula, a good tool to check the formula step by step.
Assuming F2 is 123
REPT(0,15)
Generate a string with 15 "0", that is "000000000000000"
TEXT(F2,[1])
Convert F2 into a string with 15 char. Eg. 123 > "000000000000123"
ROW(INDIRECT("1:15"))
Return an array {1;2;3;4;5;6;7;8;9;10;11;12;13;14;15}
MID([2],[3],1)
Separate [2] into an array, each element is a char {"0";"0";"0";"0";"0";"0";"0";"0";"0";"0";"0";"0";"1";"2";"3"}
[4]*{2;1;2;1;2;1;2;1;2;1;2;1;2;1;2}
Since {A;B} * {C;D} = {A*C;B*D}
{"0";"0";"0";"0";"0";"0";"0";"0";"0";"0";"0";"0";"1";"2";"3"}*{2;1;2;1;2;1;2;1;2;1;2;1;2;1;2}
={0*2;0*1;0*2;....}={0;0;0;0;0;0;0;0;0;0;0;0;2;2;6}
TEXT([5],"00")
Converts the elements in the Array to two char by adding "0" in the front. The array goes to {"00";"00";"00";"00";"00";"00";"00";"00";"00";"00";"00";"00";"02";"02";"06"}
MID([6],{1,2},1)
Note that {A,B} and {A;B} are different. {A,B} is an array with 1 row and 2 columns; {A;B} is an array with 2 rows and 1 column.
In this formula, you can imagine doing MID twice, first time we use 1 as second parameter, second time we use 2 instead.
The result is a 2-D array:
{"0","0";"0","0";"0","0";"0","0";"0","0";"0","0";"0","0";"0","0";"0","0";"0","0";"0","0";"0","0";"0","2";"0","2";"0","6"}
SUMPRODUCT(-[7])
The minus sign before [7] will force all elements in the array to convert to numbers with opposite sign.
In this example, it sums 0+0+0+...+(-2)+0+(-2)+0+(-6) = -10
I have the following equation:
=EXP(FORECAST(LN($A4), LN(OFFSET(INDIRECT($B$1 & "!B8"), MATCH($A4, INDIRECT($B$1 & "!A8:A308"), 1) - 1, COLUMN() - COLUMN($B4), 2)), LN(OFFSET(INDIRECT($B$1 & "!a8"), MATCH($A4, INDIRECT($B$1 & "!A8:A308"), 1) - 1, 0, 2))))
The calculation is performing linear interpolation in log-log space. In the evaluation of this portion:
INDIRECT($B$1 & "!A8:A308"), 1) - 1, COLUMN() - COLUMN($B4), 2)
The column difference (COLUMN() - COLUMN($B4)) results in an array (e.g., {0}). This causes a value error for the MATCH() function. If I run the INDIRECT(...) call above, then the column difference doesn't result in an array. My current solution is wrap the column difference with LARGE(..., 1), which effectively flattens the array.
The question is why does the column difference result in array, and is there a better way to deal with this?
Found it! Microsoft Office Reference says this about the COLUMN function: "If the reference argument is omitted ... the COLUMN function returns the column numbers of reference as a horizontal array."
That's the explanation. The solution, then, is to use COLUMN(A1) instead of COLUMN().