How to convert an "enumerate" to array - excel

I am trying to calculate sum(a.*b) = a[1] * b[1] + a[2] * b[2]+.....
The array a with length n is present in the sheet. Values of b though are not present directly.
To get b, there is a column of data and n indexes. b[i] = data[indexs[i]]
One way to find sum(a.*b) is to first buffer the value of b using INDEX(data, indexes) and then SUM(a * b).
I want it to be done in one cell with no buffering. So I tried replacing b with the formula of b like SUM(a * INDEX(data, indexes)). But excel considers INDEX(data, indexes) to be iterable and gives me [SUM(a * b[1]), SUM(a * b[2]), ...]
How do I tell excel to take INDEX(data, indexes) as an array?
sample

In earlier versions of Excel (pre-O365), you have to use the following manoeuvre to deliver an array using Index as described here:
=SUMPRODUCT(D3:D4*INDEX(A2:A10,N(IF({1},B2:B3))))
The curly brackets around the 1 {1} are not strictly necessary, but I tend to leave them in to signal that I am using this trick.

Related

How to make a formula shorter

I have three pieces of information: quantity, weight per piece and a limit. What I need is for the weight per piece to multiple without passing the limit and the quantity.
I made a code, but the thing is the data of quantity varies and the code used is very long.
=ROUND(IFS((F13*G13)<H13,F13*G13,((F13-1)*G13)<H13,((F13-1)*G13),
((F13-2)*G13)<H13,,((F13-3)*G13)<H13,,((F13-4)*G13)<H13,,((F13-5)*G13)<H13,,
((F13-6)*G13)<H13,(F13-6)*G13,((F13-7)*G13)<H13,(F13-7)*G13,
((F13-8)*G13)<H13,(F13-8)*G13,((F13-9)*G13)<H13,(F13-9)*G13,
((F13-10)*G13)<H13,(F13-10)*G13,((F13-11)*G13)<H13, (F13-11)*G13,
((F13-12)*G13)<H13,(F13-12)*G13,((F13-13)*G13)<H13,(F13-13)*G13,
((F13-14)*G13)<H13,(F13-14)*G13,((F13-15)*G13)<H13,(F13-15)*G13,
((F13-16)*G13)<H13, (F13-16)*G13,((F13-17)*G13)<H13,(F13-17)*G13,
((F13-18)*G13)<H13,(F13-18)*G13),-2)+200
Here is the input and expected result, if no condition matches returns #N/A.
I don't know if the +200 at the end of the formula is supposed to be included in the limit or not so just adjust the formula accordingly
=ROUND(IF(G13*F13+200>=H13,H13,F13*G13+200),-2)
Multiply the quantity and the weight and add 200.
If the result is equal to or greater than the limit, set the result to the limit.
Otherwise use the results of the quantity * weight + 200
And then round it according to your initial formula
This is what your IFS() looks like:
IFS(((F13-0)*G13)<H13,(F13-0)*G13,
((F13-1)*G13)<H13,(F13-1)*G13,
((F13-2)*G13)<H13,,
((F13-3)*G13)<H13,,
((F13-4)*G13)<H13,,
((F13-5)*G13)<H13,,
((F13-6)*G13)<H13,(F13-6)*G13,
((F13-7)*G13)<H13,(F13-7)*G13,
((F13-8)*G13)<H13,(F13-8)*G13,
((F13-9)*G13)<H13,(F13-9)*G13,
((F13-10)*G13)<H13,(F13-10)*G13,
((F13-11)*G13)<H13,(F13-11)*G13,
((F13-12)*G13)<H13,(F13-12)*G13,
((F13-13)*G13)<H13,(F13-13)*G13,
((F13-14)*G13)<H13,(F13-14)*G13,
((F13-15)*G13)<H13,(F13-15)*G13,
((F13-16)*G13)<H13,(F13-16)*G13,
((F13-17)*G13)<H13,(F13-17)*G13,
((F13-18)*G13)<H13,(F13-18)*G13)
I see three issues:
the cases for 2 up to 5 are missing. Are you sure this is correct?
it looks like (for a general 'x'):
IF ((F13-x)*G13)<H13 THEN (F13-x)*G13, you can calculate the resulting value quite easily, isn't it?
why do you stop at 18?
You can simplify it as follows:
=LET(A, A2, B, B2, C, C2, seq, SEQUENCE(19,,0),
out, IF((seq > 1) * (seq < 6), 0, (A - seq)*B),
ROUND(#FILTER(out, (A - seq)*B < C, NA()), -2) + 200)
and extend it down, or use the array version as follow in cell D2:
=LET(A, A2:A3, B, B2:B3, C, C2:C3, seq, SEQUENCE(19,,0),
MAP(A,B,C, LAMBDA(x,y,z, LET(out, IF((seq > 1) * (seq < 6), 0, (x - seq)*y),
ROUND(#FILTER(out, (x - seq)*y < z, NA()), -2) + 200))))
Here is the output:
The implicit intersection operator (#) ensures to get the first element of FILTER result, which is equivalent to get the first condition that matches. If no condition matches, then it returns NA(). The name out, has the result in the same order it should be tested via IFS. Using SEQUENCE allows to simplify the process.

Excel Dynamic Array formula to create a running product of a column

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.

Raising a number to the power of a range of numbers

The formula I would like to use looks something like this: SUMPRODUCT(x^(1:n),y^(n:1)). n=values in column A. 1:n is the exponents in forward progression from 1 to n in steps of 1. n:1 is the exponents in reverse progression from n to 1 in steps of 1. I would like the formula to be dynamic to fill in column B with the n values based on column A.
Try:
=SUMPRODUCT(5^ROW(1:100))
Or in Excel O365
=SUM(5^ROW(1:100))
As per #RonRosenfeld, a more sturdy solution could be =SUM(5^SEQUENCE(100)) in Excel 365.
EDIT: Based on OP's comments he could use (no O365):
=SUMPRODUCT(5^ROW(A1:INDEX(A:A,COUNTA(A:A))),7^LARGE(ROW(A1:INDEX(A:A,COUNTA(A:A))),ROW(A1:INDEX(A:A,COUNTA(A:A)))))
You can store the powers in a column and use the array formula:
SUM((A1:A100)^$B$1) where A column contains 5 in each cell and B column contains the range of powers you want to use. You can use an array formula in the different cell to get the answer.
Use the SERIESSUM function
The Excel SERIESSUM function returns the sum of a power series, based on the following power series expansion:
Power Series Equation
The syntax of the function is:
SERIESSUM( x, n, m, coefficients )
Where the function arguments are:
x - The input value to the power series.
n - The first power to which x is to be raised.
m - The step size that n is increased by, on each successive power of x.
coefficients - An array of coefficients that multiply each successive power of x.
The number of values in the supplied coefficients array defines the number of terms in the power series. This is illustrated in the following examples.
Example 1:
In the spreadsheet below, the Excel Seriessum function is used to calculate the power series:
5^1 + 5^2 + 5^3 + 5^4 + 5^5
formula: =SERIESSUM( 5, 1, 1, {1,1,1,1,1} )
output = 3905
Example 2:
1 * 2^1 + 2 * 2^3 + 3 * 2^5 + 4 * 2^7 + 5 * 2^9
formula: =SERIESSUM( 2, 1, 2, {1,2,3,4,5} )
output = 3186
I hope this is of help.
An Alternative Answer again. I think the correct for your case :-)
Using the SERIESSUM function allows the use of different coefficients therefore the reason for the use of the coefficients in an array. But because the coefficients are the same then this is simply a geometric progression.
The following formula will do that for you:
=n+n*(n)^(1)*(1-(n)^c)/(1-n)
where "n" is the number (5) and "c" is the number of the series (100)
This becomes:
=5+5*(5)^(1)*(1-(5)^100)/(1-5)
=SUMPRODUCT(5^ROW(A1:INDEX(A:A,COUNTA(A:A))),7^LARGE(ROW(A1:INDEX(A:A,COUNTA(A:A))),ROW(A1:INDEX(A:A,COUNTA(A:A)))))
This formula worked flawlessly!!!
Thank you #JvdV and everyone else for your efforts in helping me! GREATLY APPRECIATED!

2D vector lookup [x;y]

I've got a matrix, with two coordinates [i;j]
I'm trying to automatize a lookup:
As an example, this would have the coordinates of [1;2]
Here's a table of all the coordinates:
So here, obviously [1;2] would equate to 143,33
To simplify the issue:
I'll try to go step by step over what I'm trying to do to make the question bit less confusing.
Think of what I'm trying to do as a function, lookup(i, j) => value
Now, refer to the second picture (table)
I find all rows containing index [i] (inside column C) and then
only for those rows find row containing index [j] (inside column D ∩ for rows from previous step)
Return [i;j] value
So if u invoked lookup(2, 4)
Find all rows matching i = 2
Row 5: i = 2 ; j = 3
Row 6: i = 2 ; j = 4
Row 7: i = 2 ; j = 5
Lookup column j for j=4 from found rows
Found row 6: i = 2 ; j = 4.
Return value (offset for yij column = 143,33)
Now this isn't an issue algorhitmically speaking, but I have no idea how to go about doing this with excel formulas.
PS: I know this is reltively simple vba issue but I would prefer formulas
PSS: I removed what I tried to make the question more readable.
You can use SUMPRODUCT, which return 0 for not found values:
=SUMPRODUCT(($C$4:$C$18=$I4)*($D$4:$D$18=J$3)*$E$4:$E$18)
or AGGREGATE, which returns an error that can be hidden by the IFERROR function:
=IFERROR(AGGREGATE(15,6,(1/(($C$4:$C$18=$I12)*($D$4:$D$18=J$3)))*$E$4:$E$18,1),"")
You can use SUMIFS here assuming you will not have exact duplicate combinations of [i, j]. If you did have a duplicate combination, the amounts will be summed and placed in the corresponding cell
In cell B2 place this equation: =SUMIFS($Q$2:$Q$16,$P$2:$P$16,B$1,$O$2:$O$16,$A2) and drag across and over as needed
IF you want to convert the 0's to blanks you can nest the above formula inside a text formatter like so:
=TEXT([formula], "0;-0;;#")

masking a double over a string

This is a question in MatLab...
I have two matrices, one being a (5 x 1 double) :
1
2
3
1
3
And the second matrix being a (5 x 3 string), with spaces where no character appears :
a
bc
def
g
hij
I am trying to get an output such that a (5 x 1 string) is created and outputs the nth value from each line of matrix two, where n is the value in matrix one. I am unsure how to do this using a mask which would be able to handle much larger matrces. My target matrix would have the following :
a
c
f
g
j
Thank you very much for the help!!!
There are so many ways you can accomplish this task. I'll give you two.
Method #1 - Generate linear indices and access elements
Use sub2ind to generate a set of linear indices that correspond to the row and column locations you want to access in your matrix. You'll note that the column locations are the ones changing, but the row locations are always increasing by 1 as you want to access each row. As such, given your string matrix A, and your columns you want to access stored in ind, just do this:
A = ['a '; 'bc '; 'def'; 'g ';'hij'];
ind = [1 2 3 1 3];
out = A(sub2ind(size(A), (1:numel(ind)).', ind(:)))
out =
a
c
f
g
j
Method #2 - Create a sparse matrix, convert to logical and access
Alternatively, you can create a sparse matrix through sparse where the non-zero entries are rows vary from 1 up to as many elements as you have in ind and the columns vary like what you have given us.
S = sparse((1:numel(ind)).',ind(:),true,size(A,1),size(A,2));
A = A.'; out = A(S.');
Be mindful that you are trying to access each element in a row-major fashion, yet MATLAB will do this in a column-major format. As such, we would need to transpose our data matrix, and also take our sparse matrix and transpose that too. The end result should give you the same order as Method #1.

Resources