Related
I am trying to create a formula that checks for several things at the same time but I am having trouble with one part of it.
The formula is the following:
=IF(COUNTIFS($N$2:$N$17095,N3,$K$2:$K$17095,"<>"&"")>6,
IF((SUMPRODUCT(--(ROUND($K$2:$K$49,2)=ROUND(K3,2)))>9),"Always Late / Possible Automation",
IF(COUNTIFS($N$2:$N$17088,N3,$K$2:$K$17088,"<3.5")>0,"Delivered Earlier At Least Once",IF(COUNTIFS($N$2:$N$17088,N3,$K$2:$K$17088,">3.5")>6,"Always Late","False"))))
The first part checks how many entries in the range having the same value as it is in cell N3 have values different to blank and we want those to be more than 6.
Second part is the tough one, it is supposed to check how many values in the rounded range match the rounded value in cell K3. The issue is the formula checks the whole range and I want to check only for the values which match N3 (in essence like the CountIf works only for that value).
The rest is not so relevant.
Some example data:
![enter image description here][1]
As you see in the end of the table the formula with the rounding works but only because I have limited the data shown to 3 unique values in column N. Even here though if i have a blank it doesnt work becaus ei haven't considered it.
Thanks in advance.
Assuming you have Office 365 compatibility/Excel version, use a bunch of filters. To avoid an unwieldy formula I've extended ranges to maximum number of rows (customize as required)...
=LET(x_,$E$3:$E$25,a_,FILTER($E$3:$E$25,--(x_=E3)),b_,FILTER($B$3:$B$25,--(x_=E3)),IF(SUM(--(a_=E3)*(b_<>""))>6,IF((SUMPRODUCT(--(ROUND(b_,2)=ROUND(B3,2)))>9),"Always Late / Possible Automation",IF(SUM(--(a_=E3)*(b_<3.5))>0,"Delivered Earlier At Least Once",IF(SUM(--(a_=E3)*(b_>3.5))>6,"Always Late","False")))))
Note: filter does not appear to work too well within countifs for some reason (must be related to syntax RE: arrays vs. criteria TBC). Thus have replaced countifs(filter_range,X) eqn 'types' with sum(--(filter_range=X)) which works as intended.
Without VBA, I am trying to refer a range that starts at A2 and never ends. For example, if I want row 2 till row 5 i'd do
$A$2:$A$5
But what if I want the end to be open?
$A$2:??
Is this possible?
Depending on what's in A1 and what formula you're putting the reference into, you could simply use A:A. For example, if you wanted to sum all of the values in column A, but A1 contained a column title rather than a number, you could still write =SUM(A:A) and the title in A1 would just be ignored.
A2:A works in many formulas
hope that helps
If you want to refer to a range starting from A2 until max row (1048576 or 65536 for Excel prior to 2007), you can use this volatile formula... =OFFSET(A2,0,0,(COUNTBLANK(A:A)+COUNTA(A:A)-1),1) . Use formula as a defined range name or inside other formula which takes range as an argument (for eq SUM)...
Another option (in case your formula is in A1, so accessing A:A would create a circular reference) is:
OFFSET(A2, 0, 0, ROWS(A:A)-1)
This uses ROWS to count the total number of rows (without actually accessing the rows!), subtracts 1 (because we're starting with the second row), and uses this result as the height of a range created with OFFSET.
This is another option based on a formula, using the example locations in the OP's question:
=A2:INDEX(A:A,MAX(FILTER(ROW(A:A),IF(ISBLANK(A:A),0,1)=1)))
The components are the following:
=MAX(FILTER(ROW(A:A),IF(ISBLANK(A:A),0,1)=1))
which finds the number of the deepest row that is not blank, and
A2:INDEX(A:A,<expression 1 above>)
which relies on the expression above to make a bigger formula, which obtains a range starting from any location and ending at a location in the given column at the position obtained by this expression, 1.
This is an alternative to the others listed, and may be of interest as it differs from them in potentially substantial ways.
I can note the following characteristics:
It is not necessarily fast.
It seems to NOT be a volatile formula. This is important, as it means it won't necessarily be recalculated every time a calculation is made. However, I am not sure about the frequency of calculation, and don't fully understand its volatility status.
The uncertainty is related the use of the INDEX function (and, apparently, specifically after the : in a range). There are some resources that describe it.
INDIRECT and OFFSET functions are definitely volatile. There are a number of resources that describe performance implications of volatile functions, some of them mentioned in other SO answers. For example:
https://learn.microsoft.com/en-us/office/client-developer/excel/excel-recalculation
https://www.sumproduct.com/thought/volatile-functions-talk-dirty-to-me
http://www.decisionmodels.com/calcsecretsi.htm
https://chandoo.org/wp/handle-volatile-functions-like-they-are-dynamite/
It allows the user to not have to think about the data in certain cells (for example, A1, which may be meant to have a header, and not numbers).
It returns a range between the cell specified before the : and the last cell in the column that is non-blank. I think it should include non-numeric values in its consideration as well.
It shares some commonality in terms of the range it aims to identify with the answer by Kresimir L.: =OFFSET(A2,0,0,(COUNTBLANK(A:A)+COUNTA(A:A)-1),1).
To note: This answer applies to the version of Excel available as of the time of writing as part of Office 365 (and continually updated). However, the answer is based only on my own verification of its apparent correctness of my installation. I am not sure that all installations of Office 365 have the same software exactly; and I have the sense that some features may differ among different installations (even) of Office 365. I am not sure that this answer applies to everyone. Please test. I would appreciate feedback on your success with this approach.
This well covered in VBA as code below:
Range("A2", Range("A2").End(xlDown))
And if you want reach that in formula, it depends on the version number of your MS-Excel.
According to this reference number of all rows are in a sheet from Excel 2007 onwards are 1048576 that you can use bellow:
$A$2:$A$1048576
Because this range in formula is depended on Excels version, this may be different in future versions.
Finally, I suggest you use VBA.
Does anyone know the formula to find the value of the last non-empty cell in a column, in Microsoft Excel?
Using following simple formula is much faster
=LOOKUP(2,1/(A:A<>""),A:A)
For Excel 2003:
=LOOKUP(2,1/(A1:A65535<>""),A1:A65535)
It gives you following advantages:
it's not array formula
it's not volatile formula
Explanation:
(A:A<>"") returns array {TRUE,TRUE,..,FALSE,..}
1/(A:A<>"") modifies this array to {1,1,..,#DIV/0!,..}.
Since LOOKUP expects sorted array in ascending order, and taking into account that if the LOOKUP function can not find an exact match, it chooses the largest value in the lookup_range (in our case {1,1,..,#DIV/0!,..}) that is less than or equal to the value (in our case 2), formula finds last 1 in array and returns corresponding value from result_range (third parameter - A:A).
Also little note - above formula doesn't take into account cells with errors (you can see it only if last non empty cell has error). If you want to take them into account, use:
=LOOKUP(2,1/(NOT(ISBLANK(A:A))),A:A)
image below shows the difference:
This works with both text and numbers and doesn't care if there are blank cells, i.e., it will return the last non-blank cell.
It needs to be array-entered, meaning that you press Ctrl-Shift-Enter after you type or paste it in. The below is for column A:
=INDEX(A:A,MAX((A:A<>"")*(ROW(A:A))))
Here is another option: =OFFSET($A$1;COUNTA(A:A)-1;0)
I know this question is old, but I'm not satisfied with the answers provided.
LOOKUP, VLOOKUP and HLOOKUP has performance issues and should really never be used.
Array functions has a lot of overhead and can also have performance issues, so it should only be used as a last resort.
COUNT and COUNTA run into problems if the data is not contiguously non-blank, i.e. you have blank spaces and then data again in the range in question
INDIRECT is volatile so it should only be used as a last resort
OFFSET is volatile so it should only be used as a last resort
any references to the last row or column possible (the 65536th row in Excel 2003, for instance) is not robust and results in extra overhead
This is what I use
when the data type is mixed: =max(MATCH(1E+306,[RANGE],1),MATCH("*",[RANGE],-1))
when it's known that the data contains only numbers: =MATCH(1E+306,[RANGE],1)
when it's known that the data contains only text: =MATCH("*",[RANGE],-1)
MATCH has the lowest overhead and is non-volatile, so if you're working with lots of data this is the best to use.
Inspired by the great lead given by Doug Glancy's answer, I came up with a way to do the same thing without the need of an array-formula. Do not ask me why, but I am keen to avoid the use of array formulae if at all possible (not for any particular reason, it's just my style).
Here it is:
=SUMPRODUCT(MAX(($A:$A<>"")*(ROW(A:A))))
For finding the last non-empty row using Column A as the reference column
=SUMPRODUCT(MAX(($1:$1<>"")*(COLUMN(1:1))))
For finding the last non-empty column using row 1 as the reference row
This can be further utilized in conjunction with the index function to efficiently define dynamic named ranges, but this is something for another post as this is not related to the immediate question addressed herein.
I've tested the above methods with Excel 2010, both "natively" and in "Compatibility Mode" (for older versions of Excel) and they work. Again, with these you do not need to do any of the Ctrl+Shift+Enter. By leveraging the way sumproduct works in Excel we can get our arms around the need to carry array-operations but we do it without an array-formula. I hope someone out there may appreciate the beauty, simplicity and elegance of these proposed sumproduct solutions as much as I do. I do not attest to the memory-efficiency of the above solutions though. Just that they are simple, look beautiful, help the intended purpose and are flexible enough to extend their use to other purposes :)
Hope this helps!
All the best!
This works in Excel 2003 (& later with minor edit, see below). Press Ctrl+Shift+Enter (not just Enter) to enter this as an array formula.
=IF(ISBLANK(A65536),INDEX(A1:A65535,MAX((A1:A65535<>"")*(ROW(A1:A65535)))),A65536)
Be aware that Excel 2003 is unable to apply an array formula to an entire column. Doing so yields #NUM!; unpredictable results may occur! (EDIT: Conflicting information from Microsoft: The same may or may not be true about Excel 2007; problem may have been fixed in 2010.)
That's why I apply the array formula to range A1:A65535 and give special treatment to the last cell, which is A65536 in Excel 2003. Can't just say A:A or even A1:A65536 as the latter automatically reverts to A:A.
If you're absolutely sure A65536 is blank, then you can skip the IF part:
=INDEX(A1:A65535,MAX((A1:A65535<>"")*(ROW(A1:A65535))))
Note that if you're using Excel 2007 or 2010, the last row number is 1048576 not 65536, so adjust the above as appropriate.
If there are no blank cells in the middle of your data, then I would just use the simpler formula, =INDEX(A:A,COUNTA(A:A)).
An alternative solution without array formulas, possibly more robust than that of a previous answer with a (hint to a) solution without array formulas, is
=INDEX(A:A,INDEX(MAX(($A:$A<>"")*(ROW(A:A))),0))
See this answer as an example.
Kudos to Brad and barry houdini, who helped solving this question.
Possible reasons for preferring a non-array formula are given in:
An official Microsoft page (look for "Disadvantages of using array formulas").
Array formulas can seem magical, but they also have some disadvantages:
You may occasionally forget to press CTRL+SHIFT+ENTER. Remember to press this key combination whenever you enter or edit an array formula.
Other users may not understand your formulas. Array formulas are relatively undocumented, so if other people need to modify your workbooks, you should either avoid array formulas or make sure those users understand how to change them.
Depending on the processing speed and memory of your computer, large array formulas can slow down calculations.
Array Formula Heresy.
if you search in Column (A) use :
=INDIRECT("A" & SUMPRODUCT(MAX((A:A<>"")*(ROW(A:A)))))
if your range is A1:A10 you can use:
=INDIRECT("A" & SUMPRODUCT(MAX(($A$1:$A10<>"")*(ROW($A$1:$A10)))))
in this formula :
SUMPRODUCT(MAX(($A$1:$A10<>"")*(ROW($A$1:$A10))))
returns last non blank row number ,and indirect() returns cell value.
=INDEX(A:A, COUNTA(A:A), 1) taken from here
=MATCH("*";A1:A10;-1) for textual data
=MATCH(0;A1:A10;-1) for numerical data
Ive tried all the non-volatile versions but Not one version given above has worked.. excel 2003/2007update. Surely this can be done in excel 2003. Not as an array nor standard formula.
I either get just a blank, 0 or #value error.
So I resort to the volatile methods .. This worked..
=LOOKUP(2,1/(T4:T369<>""),T4:T369)
#Julian Kroné .. Using ";" instead of "," does NOT work! I think you are using Libre Office not MS excel?
LOOKUP is so annoyingly volitile I use it as a last resort only
For Microsoft office 2013
"Last but one" of a non empty row:
=OFFSET(Sheet5!$C$1,COUNTA(Sheet5!$C:$C)-2,0)
"Last" non empty row:
=OFFSET(Sheet5!$C$1,COUNTA(Sheet5!$C:$C)-1,0)
Place this code in a VBA module. Save. Under functions, User defined look for This function.
Function LastNonBlankCell(Range As Excel.Range) As Variant
Application.Volatile
LastNonBlankCell = Range.End(xlDown).Value
End Function
for textual data:
EQUIV("";A1:A10;-1)
for numerical data:
EQUIV(0;A1:A10;-1)
This give you the relative index of the last non empty cell in the range selected (here A1:A10).
If you want to get the value, access it via INDIRECT after building -textually- the absolute cell reference, eg:
INDIRECT("A" & (nb_line_where_your_data_start + EQUIV(...) - 1))
I had the same problem too. This formula also works equally well:-
=INDIRECT(CONCATENATE("$G$",(14+(COUNTA($G$14:$G$65535)-1))))
14 being the row number of the first row in the rows you want to count.
Chronic Clawtooth
I used HLOOKUP
A1 has a date;
A2:A8 has forecasts captured at different times, I want the latest
=Hlookup(a1,a1:a8,count(a2:a8)+1)
This uses a standard hlookup formula with the lookup array defined by the number of entries.
If you know that there are not going to be empty cells in between, the fastest way is this.
=INDIRECT("O"&(COUNT(O:O,"<>""")))
It just counts the non-empty cells and refers to the appropriate cell.
It can be used for a specific range as well.
=INDIRECT("O"&(COUNT(O4:O34,"<>""")+3))
This returns the last non empty cell in the range O4:O34.
This formula worked with me for office 2010:
=LOOKUP(2;1/(A1:A100<>"");A1:A100)
A1: the first cell
A100: refer to the last cell in comparing
I think the response from W5ALIVE is closest to what I use to find the last row of data in a column. Assuming I am looking for the last row with data in Column A, though, I would use the following for the more generic lookup:
=MAX(IFERROR(MATCH("*",A:A,-1),0),IFERROR(MATCH(9.99999999999999E+307,A:A,1),0))
The first MATCH will find the last text cell and the second MATCH finds the last numeric cell. The IFERROR function returns zero if the first MATCH finds all numeric cells or if the second match finds all text cells.
Basically this is a slight variation of W5ALIVE's mixed text and number solution.
In testing the timing, this was significantly quicker than the equivalent LOOKUP variations.
To return the actual value of that last cell, I prefer to use indirect cell referencing like this:
=INDIRECT("A"&MAX(IFERROR(MATCH("*",A:A,-1),0),IFERROR(MATCH(9.99999999999999E+307,A:A,1),0)))
The method offered by sancho.s is perhaps a cleaner option, but I would modify the portion that finds the row number to this:
=INDEX(MAX((A:A<>"")*(ROW(A:A))),1)
the only difference being that the ",1" returns the first value while the ",0" returns the entire array of values (all but one of which are not needed). I still tend to prefer addressing the cell to the index function there, in other words, returning the cell value with:
=INDIRECT("A"&INDEX(MAX((A:A<>"")*(ROW(A:A))),1))
Great thread!
If you are not afraid to use arrays, then the following is a very simple formula to solve the problem:
=SUM(IF(A:A<>"",1,0))
You must press CTRL + SHIFT + ENTER because this is an array formula.
INDEX returns a value by index position in an array and ROWS then is used to specify the last position of the array.
=LET(array,A1:A10,INDEX(array,ROWS(array)))
Also works for multiple columns when setting the parameter [column_num] of INDEX to 0:
=LET(array,A1:C10,INDEX(array,ROWS(array),0))
A simple one which works for me:
=F7-INDEX(A:A,COUNT(A:A))
Okay, so I had the same issue as the asker, and tried both top answers. But only getting formula errors. Turned out that I needed to exchange the "," to ";" for the formulas to work. I am using XL 2007.
Example:
=LOOKUP(2;1/(A:A<>"");A:A)
or
=INDEX(A:A;MAX((A:A<>"")*(ROW(A:A))))
For version tracking (adding the letter v to the beginning of the number), I found this one to work well in Xcelsius (SAP Dashboards)
="v"&MAX(A2:A500)
(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.)
I have a SUM array formula that has multiple nested IF statements, making it very inefficient. My formula spans over 500 rows, but here is a simple version of it:
{=SUM(IF(IF(A1:A5>A7:A11,A1:A5,A7:A11)-A13:A17>0,
IF(A1:A5>A7:A11,A1:A5,A7:A11)-A13:A17,0))}
As you can see, the first half of the formula checks where the array is greater than zero, and if they are, it sums those in the second part of the formula.
You will notice that the same IF statement is repeated in there twice, which to me is inefficient, but is the only way I could get the correct answer.
The example data I have is as follows:
Sample Data in spreadsheet http://clients.estatemaster.net/SecureClientSite/Download/TempFiles/example.jpg
The answer should be 350 in this instance using the formula I mentioned above.
If I tried to put in a MAX statement within the array, therefore removing the test to find where it was greater than zero, so it was like this:
{=SUM(MAX(IF(B2:B6>B8:B12,B2:B6,B8:B12)-B14:B18,0))}
However, it seems like it only calculates the first row of data in each range, and it gave me the wrong answer of 70.
Does anyone know a away that I can reduce the size of the formula or make it more efficient by not needing to repeat an IF statement in there?
UPDATE
Jimmy
The MAX formula you suggested didnt actually work for all scenarios.
If i changed my sample data in rows 1 to 5 as below (showing that some of the numbers are greater than their respective cells in rows 7 to 11, while some of the numbers are lower)
Sample Data in spreadsheet http://clients.estatemaster.net/SecureClientSite/Download/TempFiles/example2.jpg
The correct answer im trying to achive is 310, however you suggested MAX formula gives an incorrect answer of 275.
Im guessing the formula needs to be an array function to give the correct answer.
Any other suggestions?
=MAX( MAX( sum(A1:A5), sum(A7:A11) ) - sum(A13:A17), 0)
A more calculation-efficient (and especially re-calculation efficient) way is to use helper columns instead of array formulae:
C1: =MAX(A1,A7)-A13
D1: =IF(C1>0,C1,0)
copy both these down 5 rows
E1: =SUM(D1:D5)
Excel will then only recalculate the formulae dependent on any changed value, rather than having to calculate all the virtual columns implied by the array formula every time any single number changes. And its doing less calculations even if you change all the numbers.
You may want to look into the VB Macro editor. In the Tools Menu, go to Macros and select Visual basic Editor. This gives a whole programming environment where you can write your own function.
VB is a simple programming language and google has all the guidebooks you need.
There, you can write a function like MySum() and have it do whatever math you really need it to, in a clear way written by yourself.
I pulled this off google, and it looks like a good guide to setting this all up.
http://office.microsoft.com/en-us/excel/HA011117011033.aspx
This seems to work:
{=SUM(IF(A1:A5>A7:A11,A1:A5-A13:A17,A7:A11-A13:A17))}
EDIT
- doesn't handle cases where subtraction ends up negative
This works - but is it more efficient???
{=SUM(IF(IF(A1:A5>A7:A11,A1:A5,A7:A11)>A13:A17,IF(A1:A5>A7:A11,A1:A5,A7:A11)-A13:A17,0))}
What about this?
=MAX(SUM(IF(A1:A5>A7:A11, A1:A5, A7:A11))-SUM(A13:A17), 0)
Edit:
Woops - Missed the throwing out negatives part. What about this? Not sure it it's faster...
=SUM((IF(A1:A5>A7:A11,IF(A1:A5>A13:A17,A1:A5,A13:A17),IF(A7:A11>A13:A17,A7:A11,A13:A17))-A13:A17))
Edit 2:
How does this perform for you?
=SUM((((A1:A5>A13:A17)+(A7:A11>A13:A17))>0)*(IF(A1:A5>A7:A11,A1:A5,A7:A11)-A13:A17))