Need a better algorithm ( Occurence of time) - excel

The "B" column has occurence of time in it.Like these
A B
X4T00289 8/4/2011 3:12:07 AM
X4T00289 8/4/2011 3:15:07 AM
X4T00289 8/4/2011 3:18:20 AM
X4T00290 8/4/2011 3:12:37 PM
YCE00194 8/8/2011 5:12:17 AM
YCE00194 8/8/2011 5:14:07 AM
YCE00194 8/10/2011 10:12:06 PM
YCE00194 8/10/2011 10:15:16 AM
Z4W00109 8/12/2011 11:12:22 AM
Z4W00109 8/4/2011 11:58:07 AM
Z4W00109 8/4/2011 12:00:07 PM
I have taken a variant and dumped the range in it like these
var = activesheet.range("A1:B4000").value
QUESTION:
The problem is, I have to identify the consequent rows that has same ID in column A and which occured within 5 minutes and highlight them with color.Take a look at first 2 rows, They occured within 5 minutes and column A value is same for both but the 3rd row occurred after 5 minutes when compared to the first row, So that row should be ignored when highlighting .While coming back to the last 2 rows they also occured within 5 minutes, they should be highlghted with color as they occured within5 minutes. I think you got what i wanted to do. Any questions please comment and I will explain it more cleaner way.
MY APPROACH:
This is what i have tried
, I have used splitting some thing like these
temp = split(text," ")
and then compare temp(0) and temp(1) and temp(2) with consequent rows
temp(0) it has year date and month in it
temp(1) it has Time
temp(2) it has AM or PM
if temp(2) and temp(0) are equal for conesequent rows then this piece of code executes
temp_var=split(temp(1),":") again
again temp_var has temp_var(0)=hours temp_var(1)=minutes temp_var=seconds
Now I have to check hours if hours are equal then
I have to check for minutes like
(minutes - next row minutes) <= 5 then color it
This is what I have done.Im not getting any more better ideas to do it. I guess there might be some other easiest way in do it. may be some inbuilt functions which im not aware of So Let me know is this the only better way to do or any other better approach or algorithm to do it? like faster way to do it?Please help me with this

This is the Code you need, if you need any clarification or change comment here
Sub HighlightDiff()
Dim r As Integer
Dim i As Integer
Dim diff As Integer
Dim y As Integer
Dim m As Integer
Dim d As Integer
Dim h As Integer
r = 4000 ' Total No. of rows
For i = 1 To r
If (Trim(Cells(i, 1).Value) = Trim(Cells(i + 1, 1).Value)) Then
'd = Cells(i, 2).Value - Cells(i + 1, 2).Value
y = Year(Sheet1.Cells(i, 2)) - Year(Sheet1.Cells(i + 1, 2))
m = Month(Sheet1.Cells(i, 2)) - Month(Sheet1.Cells(i + 1, 2))
d = Day(Sheet1.Cells(i, 2)) - Day(Sheet1.Cells(i + 1, 2))
'h = Hour(Sheet1.Cells(i, 2)) - Hour(Sheet1.Cells(i + 1, 2))
If ((y + m + d) = 0) Then
diff = (Hour(Sheet1.Cells(i, 2)) * 60 + Minute(Sheet1.Cells(i, 2))) -
(Hour(Sheet1.Cells(i + 1, 2)) * 60 + Minute(Sheet1.Cells(i + 1, 2)))
If (diff > -5 And diff < 5) Then
Range(Cells(i, 1), Cells(i, 2)).Interior.ColorIndex = 3
End If
End If
End If
Next i
End Sub

here is the algorithm:
for each c in col B
minTime = MIN(col b where ref = current ref)
if c-minTime < 5 min then
change background
end if
next c
Note that you can get dateTime difference simply like this:
if range("onecell")-range("anothercell") < #00:05#

First off, it would be good to ensure that your datetime values in column B are formatted correctly. To do this:
Select all values in column B
Now press CTRL + 1
Select Custom and type in dd/mm/yyyy hh:mm:ss AM/PM
Now you can use the following code to loop through all id's in column A and highlight in red which ones have the same id and are within 5 mins of each other:
Sub WithinFiveMinutes()
Dim rngID As Range, id As Range, timeDiff As Long
Set rngID = Range("A1:A11") //Change for your id list e.g. A1:A4000
For Each id In rngID
If id = id.Offset(1, 0) Then
timeDiff = DateDiff("n", CDate(id.Offset(0, 1)), CDate(id.Offset(1, 1))) //"n" gives time difference in minutes...
If timeDiff >= -5 And timeDiff <= 5 Then
Range(id, id.Offset(0, 1)).Interior.ColorIndex = 3
Range(id.Offset(1, 0), id.Offset(1, 1)).Interior.ColorIndex = 3
End If
End If
Next id
End Sub

Related

Excel formula for trend?

does any one can suggest me any formula in excel to use for following question.
I have a data list with historical numbers for each particular example. I would need in column G Categoraise in 3 Categories Increasing, Decreasing, Various:
For exmaple
Column A Column B Column C Column D Column E Column F Column G
month 1 month2 month3 month4 month5 Category
Example 1 3 1 2 0 4 Various
Example 2 6 4 3 1 0 Decreasing
Example 3 0 0 3 5 10 Increasing
I am not sure that trend focmula is a correct one.
Any kind suggestion?
Use nested IFS with SUMPRODUCT:
=IF(AND(SUMPRODUCT(--(B2:E2<C2:F2)),SUMPRODUCT(--(B2:E2>C2:F2))),"Various",IF(SUMPRODUCT(--(B2:E2<C2:F2)),"increasing",IF(SUMPRODUCT(--(B2:E2>C2:F2)),"decreasing","even")))
Here is a VBA solution which can be used directly on the worksheet:
Function ClassifySequence(nums As Range) As String
Dim i As Long, minDelta As Long, maxDelta As Long
Dim delta As Variant
With Application.WorksheetFunction
maxDelta = .Min(nums) - .Max(nums)
minDelta = -maxDelta
End With
For i = 1 To nums.Cells.Count - 1
delta = nums.Cells(i + 1).Value - nums.Cells(i).Value
If delta < minDelta Then minDelta = delta
If delta > maxDelta Then maxDelta = delta
Next i
If maxDelta <= 0 Then
ClassifySequence = "Decreasing"
ElseIf minDelta >= 0 Then
ClassifySequence = "Increasing"
Else
ClassifySequence = "Various"
End If
End Function
For example,
In this code I decided to classify a constant sequence as decreasing. That could of course be changed, either to "Various" or "Increasing" or by creating a fourth category as in Scott Craner's excellent answer.

Why does DateDiff return a date and not the number of minutes?

I need to find how many minutes exist between two string.
h1 = TimeValue("06:00:00")
h2 = TimeValue("22:00:00")
res = DateDiff("n", h1, h2)
However, res = 17/08/1902 whereas the expected result is 960.
Sub calcul(hours As Variant, Optional n As Integer = 0)
i = 3
Do While (Cells(i, 0) <> "")
Dim res As Date
Dim h2 As Date
Dim h1 As Date
Dim h As Integer
If (n = 0) Then
h = 0
Else
h = Cells(i, 7).Value - 1
End If
h1 = TimeValue(hours(h)("h1"))
h2 = TimeValue(hours(h)("h2"))
res = DateDiff("n", h1, h2)
...
The problem here is how you you've defined res.
Dates and time values are numbers. Even if you see it as 30/09/2019 or 12:00:00, actually, for Excel, both cases are numbers.
First date Excel can recognize properly is 01/01/1900 which integer numeric value is 1. Number 2 would be 02/01/1900 and so on. Actually, today is 43738.
For times is the same, but the decimal parts are the hours, minutes and second. 0,5 means 12:00:00. So, actually, 43738,5 means 30/09/2019 12:00:00.
Anyways, in your case, you are obtaining time difference between 2 times in minutes. The result is 960, but you are asigning this value to a date type, so 960 is getting converted to 17/08/1902.
Dim h1 As Date
Dim h2 As Date
Dim res As Single
h1 = TimeValue("06:00:00")
h2 = TimeValue("22:00:00")
res = DateDiff("n", h1, h2)
Debug.Print res
The code above will return 960 properly. Adapt it to your needs.
UPDATE: Because DateDiff returns a Long, defining res as Single is not worth it at all. I did it because working with times, in many cases, needs decimals, but if you are using just DateDiff, then you can perfectly do res as Long or res as Integer.
Note the difference between DateDiff and a normal substraction with a simple code:
Dim time1 As Date
Dim time2 As Date
Dim res1 As Integer
Dim res2 As Single 'or double if you wish
time1 = "06:00:00"
time2 = "06:30:30"
'time difference between these 2 values are 30 minutes and 30 seconds (30,5 minutes in decimal)
res1 = DateDiff("n", time1, time2)
res2 = (time2 - time1) * 1440 '1440 is the number of minutes in a whole day
Debug.Print "With DateDiff:" & res1, "Normal: " & res2
The output of this code is:
With DateDiff:30 Normal: 30,5
Using DateDiff sometimes is not worth it. Depending on how accurate you need the result, DateDiff may compensate or not. I would suggest you to avoid it if you can (this is jut my opinion)
Hope this helps
UPDATE 2: About the code above, yes, a solution would be using DateDiff("s", time1, time2) / 60 to get the seconds transformed into minutes, but this value, because of decimals, should be assigned to a data type that allows it.

array from continuous data?

I have a quite difficult problem that i cant wrap my head around.. hope you can help me!
Lets say my data is in A1:G1 for example:
A1 B1 C1 D1 E1 F1 G1
X X 0 X X X 0
or
Y X X X X Z X
The thing i would need to come up with is how to get array from this data according to the X, BUT if like in example 1 there is 2 times X in the beginning and 3 x next to each other so the array should come out like {2;2;0;3;3;3;0} so i want the array to be 7 long and the array should show the x as number how many are next to each other.
example 2 should come out like {0;4;4;4;4;0;1}
if you can figure this out would really help me alot!
Edit:
Trying to give out better, more bigger picture of what i mean..
if data is :
A B C
1 X X
2 X X
3 X
it should come out as
A B C
1: 2 4 0
2: 0 4 2
3: 1 0 0
or in array {2\4\0;0\4\2;1\0\0}
on B1 and B2 there should be 4 because the formula should count horizontal but also vertical continuum. I tried to use usmanhaqs formula but i was not able to modify it so the count resets on every line.
Real size of the table is 7 times 7 cells.
I will use the array with another array (scoreboard which is also 7 times 7 cells, and has numbers 1, 2 or 3 on every cell) using sumproduct and it will give out the points of that player.
I appreciate your efforts on helping out a newbie learner on vba :)
For a formula solution, I can only come up with one for the special case where you have just X's and zeroes (example 1) so far:
=SUM(IF(A1:G1<>"X",0,INDEX(FREQUENCY(IF(A1:G1="X",COLUMN(A1:G1)),IF(A1:G1<>"X",COLUMN(A1:G1))),N(IF({1},SUBTOTAL(2,OFFSET(A1,0,0,1,COLUMN(A1:G1)))))+1,1)))
entered as an array formula using CtrlShiftEnter
I have wrapped it in a SUM function to demonstrate that it generates an array which can be passed to another function (result: 13) or it can be array-entered across several cells:
You can test this code
Function get_array(r As Range, match_chr As String)
Dim check_val
Dim array_value
array_value = "{"
For i = 1 To r.Count
check_value = r.Item(i)
If (check_value = match_chr) Then
j = i + 1
Do While (j <= r.Count) And (check_value = r.Item(j))
j = j + 1
Loop
array_value = array_value & WorksheetFunction.Rept(j - i & ", ", j - i)
i = j - 1
Else
array_value = array_value & "0, "
End If
Next
array_value = Left(array_value, Len(array_value) - 2) & "}"
get_array = array_value
End Function
You can use it as below
EDIT
find below another function to return an array of values that can be used in the formulae
Function get_number_array(r As Range, match_chr As String)
Dim check_val
Dim array_value
Dim number_array(1 To 50) As Long
For i = 1 To r.Count
check_value = r.Item(i)
If (check_value = match_chr) Then
j = i + 1
Do While (j <= r.Count) And (check_value = r.Item(j))
j = j + 1
Loop
For k = 1 To j - i
number_array(i + k - 1) = j - i
Next k
i = j - 1
Else
number_array(i) = 0
End If
Next
get_number_array = number_array
End Function
You have to use it same as the previous one, but it will return excel array.

Excel VBA Help w/ Type 13: Mis Match Debugging

I am working n a program where I need to store 4 individual values in one column and take the sum of 12 different columns so that I can have 4 totals over the past 12 months. The problem that I am running into is that I keep getting TYPE MISMATCH erro 13.
Sub MTBMtotals()
Sheets(8).calculate
meantot = 0
ansitot = 0
apitot = 0
othertot = 0
currmonth = Month("7/22/2013")
curryear = Year("7/22/2013")
Column = currmonth + 2
Row = (curryear - 2012) * 16 + 3
For i = 1 To 12
If Row = 2 Then
Column = 14
Row = Row - 16
End If
meantot = meantotal + Sheets(8).Cells(Row, Column)
ansitot = ansitot + Sheets(8).Cells(Row + 1, Column)
apitot = apitot + Sheets(8).Cells(Row + 2, Column)
othertot = othertot + Sheets(8).Cells(Row + 3, Column) **<------- Error:13**
Column = Column - 1
Next
Sheets(8).Cells(3, 18) = meantotal
Sheets(8).Cells(4, 18) = ansitot
Sheets(8).Cells(5, 18) = apitot
Sheets(8).Cells(6, 18) = othertot
End Sub
Thank you, any help is good help.
Maybe you can provide us an example sheet? By the way isn't it easier to use =SUM(Cell:Cell) formula and then copy the result or refer to that cells? Not sure if you need macro.

Auto calculate average over varying number values row by row

I have an Excel file with several columns in it and many rows. One column, say A has ID numbers. Another column, say G has prices. Column A has repeating ID numbers, however not all numbers repeat the same amount of times. Sometimes just once, other times 2, 3 or several times. Each column G for that row has a unique price.
Basically, I need to average those prices for a given ID in column A. If each ID was repeated the same number of times, this would be quite simple, but because they are not I have to manually do my average calculation for each grouping. Since my spreadsheet has many many rows, this is taking forever.
Here is an example (column H is the average that I am currently calculating manually):
A ... G H
1 1234 3.00 3.50
2 1234 4.00
3 3456 2.25 3.98
4 3456 4.54
5 3456 5.15
11 8890 0.70 0.95
13 8890 1.20
...
So in the above example, the average price for ID# 1234 would be 3.50. Likewise, the average price for ID# 3456 would be 3.98 and for #8890 would be 0.95.
NOTICE how rows are missing between row 5 and 11, and row 12 is missing too? That is because they are filtered out for some other reason. I need to exclude those hidden rows from my calculations and only calculate the average for the rows visible.
Im trying to write a VBA script that will automatically calculate this, then print that average value for each ID in column H.
Here is some code I have considered:
Sub calcAvg()
Dim rng As Range
Set rng = Range("sheet1!A1:A200003")
For Each Val In rng
Count = 0
V = Val.Value '''V is set equal to the value within the range
If Val.Value = V Then
Sum = Sum + G.Value
V = rng.Offset(1, 0) '''go to next row
Count = Count + 1
Else
'''V = Val.Value '''set value in this cell equal to the value in the next cell down.
avg = Sum / Count
H = avg '''Column G gets the avg value.
End If
Next Val
End Sub
I know there are some problems with the above code. Im not too familiar with VBA. Also this would print the avg on the same line everytime. Im not sure how to iterate the entire row.
This seems overly complicated. Its a simple problem in theory, but the missing rows and differing number of ID# repetitions makes it more complex.
If this can be done in an Excel function, that would be even better.
Any thoughts or suggestions would be greatly appreciated. thanks.
If you can add another row to the top of your data (put column Headers in it) its quite simple with a formula.
Formula for C2 is
=IF(A2<>A1,AVERAGEIFS(B:B,A:A,A2),"")
copy this down for all data rows.
This applies for Excel 2007 or later. If using Excel 2003 or earlier, use AVERAGEIF instead, adjusting ranges accordingly
If you can't add a header row, change the first formula (cell C1) to
=AVERAGEIFS(B:B,A:A,A1)
In my way ..
Sub calcAvg()
Dim x, y, i, y2, t, Count, Mount As Integer
Dim Seek0 As String
x = 1 '--> means Col A
y = 1 '--> means start - Row 1
y2 = 7 '--> means end - Row 19
For i = y To y2
If i = y Then
Seek0 = Cells(i, x)
t = i
Count = Cells(i, x + 6)
Mount = 1
Else
If Cells(i, x) <> Seek0 Then
Cells(t, x + 7) = Count / Mount
Count = Cells(i, x + 6)
Mount = 1
t = i
Seek0 = Cells(i, x)
Else
Count = Count + Cells(i, x + 6)
Mount = Mount + 1
End If
End If
Next
End Sub
Hope this helps ..

Resources