Compare two date/times with tolerance - excel

Essentially, I have two columns of data and I would like to know if a given value in Column A matches any value in Column B within a certain delta value.
I don't think an approximated VLOOKUP would do the trick here. The values are dates and times of day (as in 8/12/14 1:43:02 PM) and I want the delta value to be <15 seconds.
So if A1 is 8/12/14 1:43:02 PM and B156 is 8/12/14 1:43:08 PM, return a boolean.

Since comments tend to get long, here is a long comment, or a short answer.
To compare timestamps, you can write =ABS(A1-B1)<1/5760. Numeric conversion is automatic, and 1/5760=15/86400, or 15 seconds, considering that for Excel, a day equals 1.
Here is an example, for one value in column A.
My Excel is french, VRAI means TRUE. The date/time format is maybe not the same as yours, but it's only visual.
In english, the formula are:
C1: =MATCH(A1,$B$1:$B$21)
D1: =INDEX($B$1:$B$21,C1)
E1: =INDEX($B$1:$B$21,C1+1)
F1: =OR(ABS(D1-A1)<1/5760,ABS(E1-A1)<1/5760)
Of course, you don't really need 4 columns to do this, but it's easier to understand.
See also the help for MATCH function: MATCH finds the largest value that is less than or equal to lookup_value. The values in the lookup_array argument must be placed in ascending order.
So you get the index of the value just before, and you have to test this value (first part of the OR, and D1) and the next value (second part, and E1).

Try this array formula:
=IFERROR(MATCH(TRUE,ABS(A2-TIME_RANGE)<EQ_15_SEC,0),"not found")
This will return either the index of the item in column B that satisfies the 15 second rule, or "not found".
TIME_RANGE is one range of values you want to check, like column B
A2 is the candidate in column A on this row. You can fill the formula down.
EQ_15_SEC just points to a cell where I did the calculation =15/60/60/24
This is an array formula, so commit by pressing Ctrl+Shift+Enter (not just Enter)

Related

Trying to write an If statement that will show date of last item selected and change as updated

I have a series of data. That goes down the left side column of dates...
8/2, 8/3, and such. Then 2 columns over I have a column left for if the date is checks so 8/2:X, 8/3:X, 8/4: ,8/5: and such. I'm wanting to reference the date in the last field that has a X in it
A B C D
8/1 X
8/2 X
8/3 x
8/4
8/5
Trying to reference the date for the last A cell if there is an X in the D cell. So it would be for 8/3 would be the date. I'm not sure how to run with this.
I've looked through but now sure how to state an if statement
To return the date in column A that is in the same row as the last "X" (or "x") in column D:
=LOOKUP(2,1/(D:D="X"),A:A)
If you only want you cells to calculate if there's an X in column E you could write it like this:
=if(E3="x","", [put your calculation here] )
If you only care if there's anything there, not necessarily an "x" or if you're worried about case sensitivity:
=if(isblank(E3),"", [put your calculation here] )
Then just drag this formula down.
Based on what you had told me use the following formula in cell H1:
=maxifs($A$3:$A$24,$E$3:$E$24,"x")
If I understood correctly, you want to return the highest date with a X in it right? You can simulate a MAX IF with array formulas:
{=MAX(IF(E:E="X",A:A,FALSE))}
(You have to enter the formula with CTRL + SHIFT + ENTER to get those brackets and to it work properly)
The IF part inside that formula returns only a list of values (inside the TRUE part) that the condition returns true
Edit: If you're using Excel 2013 and above, you should check #Mark answer, as it is a cleaner way to do it
Just going to throw my hat in the ring here for a formula that will display the value of date that corresponds to the largest row number with an X in it in column D.
AGGREGATE will find the row number as follows:
=AGGREGATE(14,6,ROW(D:D)/(D:D="x"),1)
That can be nested inside an INDEX function to return the cell address of corresponding date which in turns display the value of the cell as follows:
=INDEX(A:A,AGGREGATE(14,6,ROW(D:D)/(D:D="x"),1))
While the above does work, AGGREGATE is performing array operations. Therefore full column references like D:D should be avoided and reduced to a smaller range that would closer match your data in order to avoid excess calculations on blank cells.
If you can ONLY enter the X consecutively without skipping a date, for instance you would not mark 8/6 X if 8/5 has no X marked, then you can find the last date using the following formula:
=INDEX(Column_Date,COUNTIF(Column_X,"X"))
The look up is case insensitive which means you can also enter lowercase x instead of uppercase X in column D or mix them up, the formula will return the same result anyway.
The solution is using COUNTIF function to locate the position of the last X in Column E of your worksheet, and then use INDEX function to return the corresponding date in Column A.
COUNTIF is a combination of COUNT and IF function. I guess this might be the "IF" function you are after.
Please note if the X is not marked consecutively day by day, you may need to use someone else' answer.
Cheers :)

i want to find count of text for a date range as criteria 1 and criteria 2 in the array

I want to count number of values (N/D) in the array (below:table: list) for criteria 1 is date range( from date and through Date) and criteria 2 is Shift A, b acros ( as shown in below table-output). I want to fill column D/N with how many times D/N occur for a date range and shift A,B,C,D?
output
From Date Through Date Shift D/N
25-May-19 26-May-19 A ?
25-May-19 26-May-19 B ?
Table- list
Dates A B C D
25-May-19 N D - -
26-May-19 N D - -
27-May-19 - D N -
INDEX(A:E,MATCH(H7:I7,A:E,0),MATCH(J7,A:E,0))
Value -?
Part of the problem you may be having is dates. Make sure your dates are excel dates and not string/text that looks like a date. Simply changing the formatting of a cell does not make it a date, it simply tells excel how to try and display the information in a cell.
Dates in excel are stored as integers and they represent the days since 1900/1/1 with that date being day 1. One of the easiest ways to test if a cell contains a date or a string is:
=ISTEXT(A1)
or
=ISNUMBER(A1)
Where A1 is the cell with the date to be tested.
If it returns TRUE for the first formula it is a string/text. FALSE means it is a number. The opposite TRUE and FALSE results apply for the second formula.
In your formula's when you have something between quotes "", it will be interpreted as a string. SO something like "<=19/05/26" mean its looking for a string less than that, not a date less than that. For doing a date comparison, either concatenate the text comparison with with a cell containing a date to compare to "<="&B2 where B2 has the date or if you want to hard code it use something like "<="&Date(19,05,26)
In order to make the following solution work, your dates all need to be stored as a number. AKA Excel serial date format.
Based on the data being layed out as per the image below, you can use COUNTIFS, INDEX, and MATCH to get the date your are looking for. I recommend find your count of D and N separately and adding them together after for a total. However if you want it in a single cell formula solution it can be achieved as well as demonstrated by the results in column N. however the formula starts to get long and can be difficult potentially read/maintain at a later date.
The core of the solution will be the COUNTIFS functions. The format of the COUNTIFS function is as follows:
COUNTIFS(Range to count 1, Criteria 1, Range to count 2, Criteria 2,...,Range to count n, Criteria n)
Let start building your formula one criteria at a time. The first Criteria will be all dates that are greater than or equal to the from date. If you only want the dates after the from date, drop the equal sign or the criteria.
=COUNTIFS($A$2:$A$4,">="&$G2,
Note the $ to lock the cell references. This is done so that when the formula gets copied, the column or row references beside the $ does not change.
Then second criteria is similar to the first except you want to grab all the dates less than or equal to the through date. Again include/drop the equal sign to suit your needs.
=COUNTIFS($A$2:$A$4,">="&$G2,$A$2:$A$4,"<="&$H2,
The next criteria will be to get all the cells that match D or N the column header. Lets just focus on D for now. The tricky part is to define which column to look in. For now lets call the column to look in XXX which will make the formula become:
=COUNTIFS($A$2:$A$4,">="&$G2,$A$2:$A$4,"<="&$H2,XXX,J$1)
OR
=COUNTIFS($A$2:$A$4,">="&$G2,$A$2:$A$4,"<="&$H2,XXX,"="&J$1)
NOTE: both formulas are the same. When no comparison operator is provided
it is taken as "=" by default.
Now in order to define XXX, INDEX and MATCH will be your friends. An important side note about INDEX is that it does not directly return the value of a cell but instead returns a cell address which in turn pulls a cell value. The basic format of INDEX is:
INDEX(Range to look in, Range's ROW to look in, Range's COLUMN to look in)
That is for a 2 dimensional range. If your range is 1 dimensional, either just a column or just a row, then only the second argument "Range's Row..." need to be provided and it represents how far down the list to go.
What gets interesting about a 2D INDEX is that when 0 is provided for ROW to look in or the Column to look in, instead of throwing an error, it instead returns all rows or columns. THIS IS IMPORTANT because you want all rows of just 1 specific column. That mean your INDEX formula is going to start to look like:
INDEX($B$2:$E$4,0,SPECIFIC COLUMN NUMBER)
So now you need to find a specific column number. That is where MATCH will be your friend. MATCH takes the following format:
MATCH(Value to find, 1D range to look in, what type of search)
You already know you are going to try and match your shift column so that will be your look up value, and the range to look in will be your column headers. The type of search you will want in this case is an exact match which is represented by 0. That means your MATCH formula will look like:
MATCH($I2,$B$1:$E$1,0)
Now to combine the various pieces, throw the MATCH formula into your INDEX and replace the "SPECIFIC COLUMN...". Your INDEX will now look like:
INDEX($B$2:$E$4,0,MATCH($I2,$B$1:$E$1,0))
And the formula above can now replace the XXX in your COUNTIFS formula and you will get:
=COUNTIFS($A$2:$A$4,">="&$G2,$A$2:$A$4,"<="&$H2,INDEX($B$2:$E$4,0,MATCH($I2,$B$1:$E$1,0)),J$1)
Place the above formula in J2 and copy the cell down and to the right.
In L2 use one of the two formulas to get the total of D and N in the date range:
=SUM(J2:K2)
OR
=J2+K2
Now to get your formula all in one cell, look at the second formula above. You can simply go to the contents of cell J2 and copy the entire formula. Then edit cell L2 and replace the cell reference for for J2 with the copied formula. Repeat the process by copy formula in K2 and replacing the reference to K2 in L2. You will wind up with a formula that looks like:
=COUNTIFS($A$2:$A$4,">="&$G2,$A$2:$A$4,"<="&$H2,INDEX($B$2:$E$4,0,MATCH($I2,$B$1:$E$1,0)),J$1)+COUNTIFS($A$2:$A$4,">="&$G2,$A$2:$A$4,"<="&$H2,INDEX($B$2:$E$4,0,MATCH($I2,$B$1:$E$1,0)),K$1)
Much longer and harder to read which is why I recommend breaking the formula down into its parts for D and N separately.
Now as an alternate method you could use SUMPRODUCT and get into array operations. Your SUMPRODUCT formula to place in I2 and copy down and right could be:
=SUMPRODUCT(($A$2:$A$4>=$G2)*($A$2:$A$4<=$H2)*(INDEX($B$2:$E$4,0,MATCH($I2,$B$1:$E$1,0))=J$1))

Why is =SUMIF(C5:C19,NOT(ISFORMULA(C5:C19))) returning zero?

I have a spreadsheet that I track my hours. Each cell initially is populated with a formula, i.e. =IF(WORKDAY(B24-1,1,holidays2019)=B24,OFFSET(C24,-1,2),0)
and then as the month progresses I enter my actual time.
In the following excerpt all values through 5/10/2019 are entered.
The formula =SUMIF(C5:C19,NOT(ISFORMULA(C5:C19))) shows zero. I do not understand why this does not work.
I appreciate any help! Column B in my spreadsheet corresponds to the dates shown below and Column C to the time entries.
Expected Result: 48.9
=SUMPRODUCT(J6:J20,--NOT(ISFORMULA(J6:J20)))
The key to this solution is the -- in front of the NOT(). A boolean that is processed by a math operator gets converted to 1 or 0. --, +0, -0, *1, /1 would have all worked to do the conversion. So now you wind up with an array of values you may want to sum being multiplied by an array of 1 and 0 to indicate the ones you want. The 1 are manual entry and the 0 are your formulas entries.
Now SUMPRODUCT performs array like calculations. As a result avoid using full column/row references inside it or you will wind up with a lot of excess calculations. Adjust the ranges in the answer to suit your needs.
Here's the MSDN definition of the Criteria in =SUMIF
criteria Required. The criteria in the form of a number, expression,
a cell reference, text, or a function that defines which cells will be
added. For example, criteria can be expressed as 32, ">32", B5, "32",
"apples", or TODAY().
Important: Any text criteria or any criteria that includes logical or
mathematical symbols must be enclosed in double quotation marks (").
If the criteria is numeric, double quotation marks are not required.
So, the reason, why your SUMIF returns 0 is, because none of the cells match the criteria, as they return a number and meanwhile they expect FALSE
Another issue here being, that the ISFORMULA will return TRUE, even when a range contains a single formula while all the rest has none. So basically you need to drag the formula down for each cell individually and sum them up only when a value is TRUE
Starting from cell D1:
=ISFORMULA(B1)
And then you can simply sum them up with the formula you provided.
=SUMIF(D1:D16,TRUE,C1:C16)
Obviously, you can hide the column D to make it more aesthetically pleasing.
Your formula fails because the criteria you're matching against, is TRUE/FALSE. Obviously the values in C5:C19 don't contain any booleans, so the sum is 0.
To solve this, you can add the correct criteria in cell D5 and below: =ISFORMULA(C5)
Then use =SUMIF(D5:D19,FALSE,C5:C19) to sum the values in column C.

MATCH and LOOKUP returning unexpected values

UPDATE WITH ANSWER:
I misunderstood the operation of MATCH and LOOKUP; they apparently do not automatically select the last value.
The updated formula is as follows:
=IF(
ISNUMBER( MATCH(2,INDEX(1/($D2=$D$1:$D1),0)) ),
($A2+$B2) - (LOOKUP(2,1/($D2=$D$1:$D1),$A$1:$A1)+LOOKUP(2,1/($D2=$D$1:$D1),$B$1:$B1))
,0 )
The main differences are that MATCH is now MATCH(2,INDEX(1/($D2=$D$1:$D1),0)) and LOOKUP is now LOOKUP(2,1/($D2=$D$1:$D1),$A$1:$A1).
Thanks barry houdini and Nanashi for their help!
I am working on what was supposed to be a simple spreadsheet, but one of my formulas is giving me unanticipated results. A screenshot of my data can be seen below:
![Spreadsheet Screenshot][1]
In column C, I am trying to get the time difference between the previous data point with the same type and the current data point. My formula (formatted for easier reading, and taken at cell C11) is as follows:
=IF(
NOT( ISNA( MATCH($D11,$D$1:$D10,1) ) ),
($A11+$B11)-(
LOOKUP($D11,$D$1:$D10,$A$1:$A10)+LOOKUP($D11,$D$1:$D10,$B$1:$B10)
),FALSE)
The cell numbering changes for the appropriate cell–for example, C10 references $D10 and the range $D$1:$D9, etc.
The conditional (NOT( ISNA( MATCH($D11,$D$1:$D10,1) ) ) checks to make sure that in column C there is a previous value that equals the current one; otherwise it returns FALSE, as it does in rows 1 and 12.
The calculation ($A11+$B11)-(LOOKUP($D11,$D$1:$D10,$A$1:$A10)+LOOKUP($D11,$D$1:$D10,$B$1:$B10) takes the current row date and time and subtracts the date and time in the row corresponding with the previous instance of the same type (the LOOKUP function is supposed to return the last occurrence of a value in an array, according to the documentation).
My issue is this: after row 12, the MATCH and LOOKUP functions are all picking up row 11 as the last row in which column C has the value of 1. I've tried testing them separately, and both functions return their appropriate values from row 11. For example, putting the following formula in E16: =MATCH(1,$D$1:$D16,1) returned 11 when I would expect the value to be 16.
What am I doing wrong?
The problem here is that MATCH is not programmed to return the index of the latest matching value. That said, a formula that gets this for us will solve the problem.
Enter the following formula in C2 and drag down:
=MOD((B2)-INDEX(B:B,SUMPRODUCT(MAX(($D$1:$D1=D2)*ROW($D$1:$D1)))),1)
Broken down, this is what each part does:
=SUMPRODUCT(MAX(($D$1:$D1=D2)*ROW($D$1:$D1))) 'Returns the row of the latest match.
=INDEX(B:B, SUMPRODUCT...) 'Returns the matching cell in Column B.
=MOD(B2 - INDEX(...),1) 'Gets the absolute difference, no need to add A and B.
Result:
Let us know if this helps.
EDIT: Changed formula as per Barry's correction.
Neither MATCH nor LOOKUP will find the "last occurrence" as you require with unsorted data. Try these versions for last row/match:
=MATCH(2,INDEX(1/($D11=$D$1:$D10),0))
=LOOKUP(2,1/($D11=$D$1:$D10),$B$1:$B10)
....for your specific requirement you can get both date and time in one LOOKUP, e.g.
=$A11+$B11-LOOKUP(2,1/($D11=$D$1:$D10),$A$1:$A10+$B$1:$B10)
which explicitly uses the dates in column A - if you have data for every day, so you can always assume that change of day is to the next day then you can leave out the date references and use MOD like this:
=MOD($B11-LOOKUP(2,1/($D11=$D$1:$D10),$B$1:$B10),1)

VLookup with empty cells

Here are my 2 columns
A B
Spain [EMPTY]
France Euros
Spain Euros
In another cell, I would like to write a formula that would write the currency of each country. like if I have:
C1=Spain, C2=France,
I would want to have
D1=Euros, D2=Euros.
I tried it with VLOOKUP, but it gave me
C1=[Empty], C2=Euros
Thank you very much for your time
As others have commented, VLOOKUP will return the first match it finds, thats why you get [EMPTY] for Spain.
You can work around this by adding an intermediate column. Lets assume you insert a column B with formula =IF(C1="","",A1) and copy down for all used rows.
Column D is Spain, France etc
Column E is now =VLOOKUP(D1,B:C,2,0)
IF you can SORT your columns, sort by Column A then by B descending (Z-A) then use the vlookup you're trying to use. THis will put the country with a value 1st so V-Lookup will return a vale instead of empty. When empty is returned, its becuase EVERY single instance of that entry in column A has an empty value. However if the entry in A has Multiple values, it will pick the first from descending order.
So do you have situations in which a entry in column A has multiple values beyond 1 currency and a blank entry?
However this has a pitfall in that the Order now matters so if data is added to this spreadsheet a reorder must occur each time.
I'm more a VBA than formula guy but this (horrible!) array formula will return the first non blank match
in D1 put
=IF(MAX(IF(--(A$1:A$6=C1)*LEN(B$1:B$6)>0,ROW(A$1:A$6),0))>0,INDEX(B$1:B$6,MAX(IF(--(A$1:A$6=C1)*LEN(B$1:B$6)>0,ROW(A$1:A$6),0))),"no match")
and press Shift - Ctrl - Enter together
how it works
IF(--(A$1:A$6=C1)*LEN(B$1:B$6)>0,ROW(A$1:A$6),0)) checks that A1 matches each of A1 to A6, and correspondingly whether B1 to B6 is non-empty.
If both these conditions are TRUE then the row number of these matches is placed in an array, if FALSE the formula returns zero. so the first array id {0,0,3,0,0,0}
If the MAX of this array is not zero then the formula returns this cell position from the B column using INDEX in INDEX(B$1:B$6,MAX(IF(--(A$1:A$6=C1)*LEN(B$1:B$6)>0,ROW(A$1:A$6),0)))
Else there is "no match"
I will shoot this to a formula genius called Barry Houdini to see how much he can shorten it
Enlarged sample (including a true blank result) below
I feel like a fraud here.....don't ask me any VBA questions!
I note that use of MAX actually means that your formula (and chris' amended version) actually gives the last non-blank match. You can also do that with a non-CSE LOOKUP formula, i.e.
=LOOKUP(2,1/(B$2:B$6<>"")/(A$2:A$6=C1),B$2:B$6)
which will return #N/A if there's no match
for the text "no match" instead then, assuming formula returns text values you can use 2003 compatible
=LOOKUP("zzz",IF({1,0},"No match",LOOKUP(2,1/(B$2:B$6<>"")/(A$2:A$6=C1),B$2:B$6)))
For first non-blank match you can use INDEX/MATCH in a similar way, i.e.
=INDEX(B$2:B$6,MATCH(1,(B$2:B$6<>"")*(A$2:A$6=C1),0))
confirmed with CTRL+SHIFT+ENTER

Resources