Why this array formula doesn't work? - excel

On the illustration all formulas are array. The range that each formula spans is bordered, and the first formula on each block is written on the top of that block.
Range A4:A103 is an input vector (which is numeric), range C4:G23 is a given (input) permutation of the rows of A4:A103 (necessarily positive non-zero integer numbers not greater then the length of the input vector).
Let us I interpret the permutation matrix as set of rows.
How to compute for each row in a constant number of cells the minimal number in the input vector? By the constant number of cells, I mean solution, that would require fixed number of cells for each row, regardless of the number of columns in permutation. (In the production case each dimension is much, much bigger; there is about 100 columns in the permutation matrix.)
I don't ask for VBA solutions. If it is necessary the solution can use a free and publicly available Excel add-on, like MoreFunc, but I'd prefer to keep it vanilla Excel 2007 or later.
I thought that the formula {=MIN(INDEX(INDIRECT($A$2);$C4:$G4))} would solve my problem. Surprisingly, Excel seems to not take into account the array nature of the formula, and evaluates it as if it was written as =MIN(INDEX(INDIRECT($A$2);$C4) which is equivalent to dysfunctional =INDEX(INDIRECT($A$2);$C4).
On the other hand, we can see the argument to the MIN is understood as array in the range I4:M4.

INDEX works in some strange ways!
Normally INDEX can't return an array - although you seem to have found the one exception to that - when it's an array formula entered in a range.
You should be able to use OFFSET to return the required array that will work within MIN, i.e. with this formula
=MIN(N(OFFSET(INDIRECT($A$2);$C4:$G4-1;0)))
confirmed with CTRL+SHIFT+ENTER

Related

Excel: Make a dynamic formula that counts a specified max sum of X consecutive days

I am trying to make a formula that could count the max sum of any number of consecutive days that I indicate in some cell. Here is the dataset and the formula:
Dataset
The formula that calculates the maximum sum of three consecutive days:
=MAX(IFERROR(INDEX(
INDEX(E2:AI2,0)+
INDEX(F2:AI2,0)+
INDEX(G2:AI2,0),
0),""))
As you can see the number of days here is determined by the number of rows in the formula that start with "Index". The only difference between these rows is the letters (E, F, G). Is there any way I could reference a cell in which I could put a number for those days, instead of adding more rows to this formula?
Another approach avoding use of Offset is to use Scan to generate an array of running totals, then subtract totals which are N elements apart (where N is the number of consecutive cells to be added):
=LET(range,E2:AI2,
length,A1,
runningTotal,SCAN(0,range,LAMBDA(a,b,a+b)),
sequence1,SEQUENCE(1,COLUMNS(range)-length+1,A1),
sequence2,SEQUENCE(1,COLUMNS(range)-length+1,0),
difference,INDEX(runningTotal,sequence1)-IF(sequence2,INDEX(runningTotal,sequence2),0),
MAX(difference))
The answer here was posted by another user on another website, so I will repost it here:
One way to achieve this without relying on a VBA solution would be to use the BYCOL() function (available for Excel for Microsoft 365):
=BYCOL(array, [function])
The array specifies the range to which you want to apply your function, and the function itself is specified in a lambda statement. In the end, you want to get the minimum value of the sum of x consecutive days. Assuming that your data is stored in the range E2:AI2 and the number of consecutive days is stored in cell A1, the function looks like this:
=MIN(BYCOL(E2:AI2,LAMBDA(col,SUM(OFFSET(col,,,,A1)))))
The MIN() part ensures that you get only the smallest sum of the array (all sums of the x consecutive values) returned. The array is simply the range in which your data is stored; it is named in the lambda argument col and consequently used by its name. In your case, you want to apply the sum function for, e.g., x = 4 consecutive days (where 4 is stored in cell A1).
However, with this simple specification, you run into the problem of offsetting beyond cells with values toward the right end of the data. This means that the last sum you get would be 81.8 (value on 31 Jan) + 3 times 0 because the cells are empty. To avoid this, you can combine your function with an IF() statement that replaces the result with an empty cell if the number of empty cells is greater than 0. The adjusted formula looks like this:
=MIN(BYCOL(E2:AI2,
LAMBDA(col,IF(COUNTIF(OFFSET(col,,,,A1),"")>0,"",SUM(OFFSET(col,,,,A1))))))
If you do not have the Microsoft 365 version, there are two approaches that would also work. However, the two approaches are a bit more tedious, especially for cases with multiple days (because the number of days can not really be set automatically; except for potentially constructing the ranges with a combination of ADDRESS() and INDIRECT()), but I would still argue a bit neater than your current specification:
=MIN(INDEX(E2:AF2+F2:AG2+G2:AH2+H2:AI2,0))
=SUMPRODUCT(MIN(E2:AF2+F2:AG2+G2:AH2+H2:AI2))
The idea regarding the ranges is the same in both scenarios, with a shift in the start and end of the range by 1 for each additional day.
Another approach getting to the same result:
=LET(range,E2:AI2,
cons,4,
repeat,COLUMNS(range)-cons+1,
MAX(
BYROW(SEQUENCE(repeat,cons,,1)-INT(SEQUENCE(repeat,cons,0,1/cons))*(cons-1),
LAMBDA(x,SUM(INDEX(range,1,x))))))
This avoids OFFSET (volatile, slowing your file down) and the repeat value, consecutive number and/or the range are easily changeable.
Hope it helps (I answered to the max sum, as stated in the title). Change max to min to get the min sum result.
Edit:
I changed the repeat part in the formula to be dynamic (max number of consecutive columns in range), but you can replace it by a number or a cell reference.
The cons part can also be linked to a cell reference.
Also found a big in my formula which is fixed.

Skip certain calculated values (not cells themselves) in an AVERAGE or STDEV Excel formula based on a criterion

I have a series of paired measurements (two for each sample). I need to calculate the mean for each pair and then the general mean based on those pairwise averaged values, but I can only include those pairwise means that don't exceed a certain threshold.
I'm trying to work out an excel formula for the general mean that takes into account all of the above, without having to create additional rows/columns.
Now, the formula
=AVERAGE(IF(ABS(A1-A2)<$C$1,AVERAGE(A1,A2)),IF(ABS(A7-A8)<$C$1,AVERAGE(A7,A8)), ... )
... doesn't do the job because it replaces with zeros those pairwise means that exceed the threshold (value of $C$1), while they just need to be skipped. "AVERAGEIF" doesn't work because it doesn't accept non-contiguous ranges (at least when they are entered as such), using "INDIRECT" to build non-contiguous ranges doesn't help either because it doesn't accept formulas. Any help appreciated.
Not using helper column here is making formula look ugly but will give you the desired result. Have a try.
=AVERAGE(IF(CHOOSE({1;2;3},A1-A2,A4-A5,A7-A8)<C1,CHOOSE({1;2;3},AVERAGE(A1,A2),AVERAGE(A4,A5),AVERAGE(A7,A8))))
This is an array formula so commit it by pressing Ctrl+Shift+Enter.
See image to see the difference in the result from above formula and formula in question that is yielding incorrect result as you mentioned.
This is where Excel has some mysterious behaviour I think.
My first thought was to make each if statement return "" if the condition wasn't satisfied
=AVERAGE(IF(ABS(A1-A2)<$C$1,AVERAGE(A1,A2),""),IF(ABS(A7-A8)<$C$1,AVERAGE(A7,A8),""))
but this just gives you an error.
However, if you make it return a reference to an empty cell (say B1) it works fine
=AVERAGE(IF(ABS(A1-A2)<$C$1,AVERAGE(A1,A2),$B$1),IF(ABS(A7-A8)<$C$1,AVERAGE(A7,A8),$B$1))

Error when finding MAX ABS using indirect function in VLOOKUP

I am trying to set up a sheet that I can use as a template to sort data, find perticular values and create a chart. Everything is working fine for the maximum magnitude and average magnitude values I am looking for. I am running into an issue in the formula in the images attatched. Below is the full code.
=VLOOKUP(MAX(ABS(INDIRECT("J"&P3&":J"&Q3))),INDIRECT("J"&P3&":M"&Q3),4,FALSE)
With this code I am fining the maximum value in the vertical direction, then returning the corresponding magnitude value.
When I was just inputting the cell values it was working fine (taking a long time but it was working) however to make it quick and easy to apply to the other data files (they are in the same layout with the same number of values but have different values) I have adjusted it to the above formula.
In the P and Q columns I have found the row of the top and bottom of the ranges I am interested in and then have substituted these values into my working formula.
This method works great for the average magnitude and maximum magnitude so I know it should work, but when used the formula above it finds the incorrect value. For some reason it evaluates an array to a single value that is the second in the list.
1: evaluated formula before the miss-step (the array that is the range I am looking for)
2: evaluated formula after the miss-step (3.5 which is not close to the maximum absolute value)
Any help would be great.
You cannot convert the max(abs(...)) and use that for the lookup; that only works for positive numbers. You need to pass processing to a second VLOOKUP if the first fails with IFERROR.
You would be better off with a slightly more advanced sub-formula that can retrieve the absolute maximum than using an array formula to achieve the same.
Additionally, the non-volatile INDEX can replace the volatile INDIRECT.
As a non-array formula,
=iferror(vlookup(max(max(index(j:j, p3):index(j:j, q3)), abs(min(index(j:j, p3):index(j:j, q3)))), index(j:j, p3):index(m:m, q3), 4, false),
vlookup(-max(max(index(j:j, p3):index(j:j, q3)), abs(min(index(j:j, p3):index(j:j, q3)))), index(j:j, p3):index(m:m, q3), 4, false))
In other words, if you cannot find the max(abs(...)) then look for the -max(abs(...)).

Excel, Array Formulas, N/A outside of range, and ROW()

I have a problem with ROW() in an array formula in Excel 2013.
Example:
I make a named range, called 'input', say 4 cells wide and 10 high. Then I make an array formula =ROW(input) one cell wide, 15 cells high.
I get 10 numbers - the first is the first row of input, and the rest count up from that, and then 5 #N/A follow. This is as it should be.
If instead of =ROW(input) I try one of the following:
=IFERROR(ROW(input),"x")
or
=IF(ISNA(ROW(input)),"x",ROW(input))
to catch the #N/As then what I expect to see is ten numbers, and then five x's. Instead I still see ten numbers and then five #N/As.
Can anyone tell me how to solve this problem? I want to get ten numbers, and then five x's.
And of lesser importance but just for curiosity (or in case it comes up in another case), why does this happen?
Why do I want to do this? It's part of a larger formula that I have simplified for this question.
I believe that, in such clauses, Excel gives precedence to the artificial expansion of the reference to match that of the worksheet range selected (which it will always do by filling with #N/As) over first resolving the IF clause over the array.
So, whereas "normally" (e.g in a single-cell array formula), e.g.:
=SUM(IF(ISNA(ROW(input)),"YES","NO"))
would, effectively, coerce Excel into expanding the single value_if_true and value_if_false parameters of the IF function into syntactically-appropriate arrays of those values, viz:
=SUM(IF({FALSE;FALSE;FALSE;FALSE;FALSE},{"YES","YES","YES","YES","YES"},{"NO","NO","NO","NO","NO"}))
i.e.:
=SUM({"NO";"NO";"NO";"NO";"NO"})
with multi-cell array formulas, e.g. your:
=IF(ISNA(ROW(input)),"YES","NO")
entered over a 10-cell range, is NOT first resolved thus:
=IF(ISNA({1;2;3;4;5;#N/A;#N/A;#N/A;#N/A;#N/A}),{"YES";"YES";"YES";"YES";"YES";"YES";"YES";"YES";"YES";"YES"},{"NO";"NO";"NO";"NO";"NO";"NO";"NO";"NO";"NO";"NO"})
(In which the the value_if_true and value_if_false parameters are first re-dimensioned in accordance with the dimension of the worksheet range in which the array is being entered.)
i.e.:
=IF({FALSE;FALSE;FALSE;FALSE;FALSE;TRUE;TRUE;TRUE;TRUE;TRUE},{"YES";"YES";"YES";"YES";"YES";"YES";"YES";"YES";"YES";"YES"},{"NO";"NO";"NO";"NO";"NO";"NO";"NO";"NO";"NO";"NO"})
i.e.:
={"NO";"NO";"NO";"NO";"NO";"YES";"YES";"YES";"YES";"YES"}
but rather as:
=IF(ISNA({1;2;3;4;5;#N/A;#N/A;#N/A;#N/A;#N/A}),{"YES";"YES";"YES";"YES";"YES";#N/A;#N/A;#N/A;#N/A;#N/A},{"NO";"NO";"NO";"NO";"NO";#N/A;#N/A;#N/A;#N/A;#N/A})
(The value_if_true and value_if_false parameters first being re-dimensioned in accordance rather with the dimensions of the Named Range input.)
i.e.:
=IF({FALSE;FALSE;FALSE;FALSE;FALSE;TRUE;TRUE;TRUE;TRUE;TRUE},{"YES";"YES";"YES";"YES";"YES";#N/A;#N/A;#N/A;#N/A;#N/A},{"NO";"NO";"NO";"NO";"NO";#N/A;#N/A;#N/A;#N/A;#N/A})
i.e.:
{"NO";"NO";"NO";"NO";"NO";#N/A;#N/A;#N/A;#N/A;#N/A}
Hope that helps a bit.
Regards

reversing rows in excel formulas

(A) SumProduct( A1:A3,B1:B3) == A1*B1 + A2*B2 + A3*B3
Instead, what I'm after is
(B) SumProduct( A1:A3, Reverse(B1:B3)) == A1*B3 + A2*B2 + A3*B1
Is there a clean way to achieve this in excel 2003 / excel 2007 ? The natural ordering of these values is A1->A3 and B1->B3, so reversing the meanings of the cells is unsatisfactory; but creating a reversed copy of B1:B3 elsewhere in the worksheet seems clumsy.
Check the topic "Transposing A List Of Data" in http://www.cpearson.com/EXCEL/lists.htm
I chanced upon this question because I was trying to find an answer to this very question, in particular a solution to be used with SUMPRODUCT.
Previous post with link was useful, and the following has been devised. Please, note that for simplicity and clarity original references have been changed to make spreadsheet formulas easier to understand. Necessary changes can be achieved by using INDIRECT and R1C1 reference style if reversal needs to be applied to columns (INDIRECT, COLUMN and COLUMNS function documentation would prove useful).
In Excel (checked in Excel 2010), the formula that SUMPRODUCTs an array in A6:A8 with the reversal of the array in B6:B8 (let's say B8:B6) could be
=SUMPRODUCT(A6:A8,N(OFFSET(B6:B8,ROWS(B6:B8)-ROW(INDIRECT("A1:A"& ROWS(B6:B8))),0)))
The reversing part for Excel is N(OFFSET(B6:B8,ROWS(B6:B8)-ROW(INDIRECT("A1:A"& ROWS(B6:B8))),0)), it can also be used as a multi-cell array formula (ctrl-shift-enter) anywhere - the array to be reversed is in B6:B8.
Brief explanation: N() is necessary to convert the references returned by OFFSET into an array of values that can be used inside SUMPRODUCT. (OFFSET without N() render the formula inoperative within SUMPRODUCT, and it converts most non numbers into 0, as expected it converts TRUE to 1.)
OFFSET takes the array on the spreadsheet and puts the values in reverse order. (In this case, offset original at 3-1=2 goes new array position 0, 3-2=1 goes to 1, and finally 3-3=0 goes to 2.) ROW() function creates an array of consecutive numbers that can be subtracted from the length of the total array [please note, no final S, and A1:A3 has been used to obtain {1,2,...}, up to the number of rows in the original array/range.]
This formula does NOT work in OpenOffice/LibreOffice spreadsheet application. But INDEX can with an analogue approach becoming even more flexible.
OpenOffice/LibreOffice can use the same approach but with the INDEX function. OpenOffice/LibreOffice solution does not work in Excel as Excel does not accept arrays in INDEX's row_num/col_num (any array as argument there becomes the single top element of the array).
In OpenOffice/LibreOffice (checked in Apache OpenOffice 4), the formula that SUMPRODUCTs an array in A6:A8 with the reversal of the array in B6:B8 could be
=SUMPRODUCT(A6:A8,INDEX(B6:B8,1+ROWS(B6:B8)-ROW(INDIRECT("A1:A"& ROWS(B6:B8))),0))
The reversing part for OpenOffice is INDEX(B6:B8,1+ROWS(B6:B8)-ROW(INDIRECT("A1:A"& ROWS(B6:B8))),0), it can also be used as a multi-cell array formula (ctrl-shift-enter) anywhere - the array to be reversed is in B6:B8.
Excel version with this approach (thanks, XOR LX: see here) which might be very useful as INDEX can take arrays whereas OFFSET can only take references to a worksheet:
=SUMPRODUCT(A6:A8,INDEX(B6:B8,N(INDEX(1+ROWS(B6:B8)-ROW(INDIRECT("A1:A"& ROWS(B6:B8))),,)),0))
[As it happens with Excel's OFFSET version above, the formula with the N(INDEX([...],,)) modification does not work in OpenOffice, the wrapping function must be taken out if using that application.]
The approach is analogue to the one used in Excel for OFFSET. Take into account that INDEX uses indexes starting with 1 whereas OFFSET starts with 0. As it happens with the previous case, an array is dynamically created from the row numbers in A1:A3 to be used as both template for the array and position change index.
This very late answer is unlikely to help the original poster, but it might save time to future users with a similar question.
I cannot see a solution that doesn't involve (a) custom functions in VBA (or similar) or (b) an extra column with partial results.
If you don't like column C becoming a (hidden) reverse list, would you accept column C becoming a list like: A1*B3, A2*B2, A3*B1, which could then be summed? It would be possible to use a similar formula to the one mentioned in #e.tadeu's answer to obtain this (using OFFET and ROW functions.)

Resources