Excel - Understanding a SUMPRODUCT formula - excel

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

Related

How to find out Max<List<List>> in Excel?

I'd like to find out largest sum of numbers separated by empty row. In this example I am looking to get number 6 (3+3)
1
1
2
2
3
3
Brute forcing this I would =MAX(SUM(A1:A2),SUM(A4:A5),SUM(A7:A8)) which does the job but obviously not practical. How can I express above more elegantly without hardcoding anything?
Thinking out loud, I would like to
Ingest all numbers, split by empty row into some kind of List<List>
Iterate over this list, sum numbers in child list and pick a winner
How can this be done in Excel?
There are multiple ways of doing it, this is just one of them. In cell C1 you can put the following formula:
=LET(set, A1:A9, matrix, 1*TEXTSPLIT(SUBSTITUTE(TEXTJOIN(",",
FALSE, set),",,",";"),",",";", TRUE),m, COLUMNS(matrix), ones, SEQUENCE(m,1,,0),
MAX(MMULT(matrix, ones))
)
and here is the output:
Note: The third input argument of TEXTSPLIT set to TRUE ensures you can have more than one empty row in the middle of the range. The second input argument of TEXTJOIN set to FALSE is required to ensure to generate of more than one comma (,), which is our condition to replace by the row delimiter (;) so we can split by row and columns. MMULT requires numbers and TEXTSPLIT converts the information into texts. we need to coerce the result into a number by multiplying it by 1.
The formula follows the approach you suggested, you can test the intermediate step. Instead of having as output MAX result the variable you want to verify, for example:
=LET(set, A1:A9, matrix, 1*TEXTSPLIT(SUBSTITUTE(TEXTJOIN(",",
FALSE, set),",,",";"),",",";", TRUE),m, COLUMNS(matrix), ones, SEQUENCE(m,1,,0),
TMP, MAX(MMULT(matrix, ones)), matrix
)
will produce the following output:
1 1
2 2
3 3
An alternative to MULT is to use BYROW array function (less verbose):
=LET(set, A1:A8, matrix, 1*TEXTSPLIT(SUBSTITUTE(TEXTJOIN(",",
FALSE, set),",,",";"),",",";", TRUE),MAX(BYROW(matrix, LAMBDA(m, SUM(m))))
)

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.

Sumproduct with OR and Right function

In column A are IDs like "1.23.1". The first and last numbers are the ones I use to identify the IDs I am looking for. The middle numbers range from 1 to 999, so IDs could be "1.1.1" OR "1.231.1. This information is only important to show that not all IDs have the same amount of characters.
I need to add the numbers from column B that match the IDs I am looking for.
A ----------- B
1.21.1 ------------5
1.314.2 ----------6
2.2.1 -------------3
I am getting a #VALUE! error with the following formula.
=SUMPRODUCT(OR((RIGHT(A4:A6,1)="1")+0,RIGHT(A4:A6,1)="2")+0,LEFT(A4:A6,1)="1",B4:B6
I would like to add the 5 and 6 because their IDs both end with 1 or 2 and start with 1.
Example
=SUMPRODUCT((LEFT(A4:A6)="1")*((RIGHT(A4:A6)="1")+(RIGHT(A4:A6)="2"))*B4:B6)
Using the OR function will only return a single value; hence your arrays will not be all the same length; hence the #VALUE! error.
Using addition will return an array of the OR test for each cell, instead of a single OR for the entire array.
If you want to use the function with separate arrays, where you convert each test array to it's numeric equivalent, (as in your example) you can use:
=SUMPRODUCT(N(LEFT(A4:A6)="1"),N((RIGHT(A4:A6)="1")+(RIGHT(A4:A6)="2")),B4:B6)

How to print a number within a string in matlab

I would like to use the command text to type numbers within 57 hexagons. I want to use a loop:
for mm=1:57
text(x(m),y(m),'m')
end
where x(m) and y(m) are the coordinates of the text .
The script above types the string "m" and not the value of m. What am I doing wrong?
Jubobs pretty much told you how to do it. Use the num2str function. BTW, small typo in your for loop. You mean to use mm:
for mm=1:57
text(x(mm),y(mm),num2str(mm));
end
The reason why I've even decided to post an answer is because you can do this vectorized without a loop, which I'd also like to write an answer for. What you can do place each number into a character array where each row denotes a unique number, and you can use text to print out all numbers simultaneously.
m = sprintfc('%2d', 1:57);
d = reshape([m{:}], 2, 57).';
text(x, y, d);
The (undocumented!) function sprintfc takes a formatting specifier and an array and creates a cell array of strings where each cell is the string version of each element in the array you supply. In order to ensure that the character array has the same number of columns per row, I ensure that each string takes up 2 characters, and so any number less than 10 will have a blank space at the beginning. I then convert the cell array of strings into a character array by converting the cell array into a comma-separated list of strings and I reshape the matrix into an acceptable form, and then I call text with all of the pairs of x and y, with the corresponding labels in m together on the screen.

Counting the occurence of substrings in matlab

I have a cell, something like this P= {Face1 Face6 Scene6 Both9 Face9 Scene11 Both12 Face15}. I would like to count how many Face values, Scene values, Both values in P. I don't care about the numeric values after the string (i.e., Face1 and Face23 would be counted as two). I've tried the following (for the Face) but I got the error "If any of the input arguments are cell arrays, the first must be a cell array of strings and the second must be a character array".
strToSearch='Face';
numel(strfind(P,strToSearch));
Does anyone have any suggestion? Thank you!
Use regexp to find strings that start (^) with the desired text (such as 'Face'). The result will be a cell array, where each cell contains 1 if there is a match, or [] otherwise. So determine if each cell is nonempty (~cellfun('isempty', ...): will give a logical 1 for nonempty cells, and 0 for empty cells), and sum the results (sum):
>> P = {'Face1' 'Face6' 'Scene6' 'Both9' 'Face9' 'Scene11' 'Both12' 'Face15'};
>> sum(~cellfun('isempty', regexp(P, '^Face')))
ans =
4
>> sum(~cellfun('isempty', regexp(P, '^Scene')))
ans =
2
Your example should work with some small tweaks, provided all of P contains strings, but may give the error you get if there are any non-string values in the cell array.
P= {'Face1' 'Face6' 'Scene6' 'Both9' 'Face9' 'Scene11' 'Both12' 'Face15'};
strToSearch='Face';
n = strfind(P,strToSearch);
numel([n{:}])
(returns 4)

Resources