I have a document with the following:
FullDateTime FullDate FullTime Day Month Year Hour Minute Second
dd/mm/yyyy hh:mm:ss AM/PM
and I would like to fill in the other columns using macros to split the first column and place the whole date, whole time, day, month, year, hour, minute and second in the other columns. FullDateTime is every five minutes and I want to the DateTime to run for a whole year. I imagine the code to look something like:
Sub Func()
Dim 5mindays as Integer = 12*24*365
Dim x As Integer
Dim date
Dim time
For x = 1 To 5mindays
Split(," ")
Split(,"/")
Split(,":")
.Offset(0,1) = date(0)
...
.Offset(0,8) = time(2)
Add the next FullDateTime field below the existing one (adding 5 minutes)
Next
But have no idea how to actually do it. Please give me some ideas on how to solve this. Thanks!
Try after setting the correct worksheet name and year to process,
Option Explicit
Sub funk()
Dim dt As Long, yr As Long, tm As Long, dttm As Double
yr = 2018
dt = DateSerial(yr, 1, 1)
With Worksheets("sheet6")
Do While Year(dt) = yr
Do While TimeSerial(0, tm * 5, 0) < 1
dttm = dt + TimeSerial(0, tm * 5, 0)
.Cells(tm + 1 + (dt - DateSerial(yr, 1, 1)) * 288, "A").Resize(1, 9) = _
Array(dttm, dt, dttm - dt, _
Day(dt), Month(dt), yr, _
Hour(dttm), Minute(dttm), 0)
tm = tm + 1
Loop
tm = 0
dt = dt + 1
Loop
With .Range(.Cells(1, "A"), .Cells(.Rows.Count, "I").End(xlUp))
.Columns("A").NumberFormat = "dd/mm/yyyy hh:mm:ss AM/PM"
.Columns("B").NumberFormat = "dd/mm/yyyy"
.Columns("C").NumberFormat = "hh:mm:ss"
.Columns("D:I").NumberFormat = "0"
End With
End With
End Sub
I need to find the average of a number found in column F if a particular value is found in any of the other columns in the sheet.
For instance: I have the following in a range...
A B C D E F
Red Bill Jack Ruby Bill 250
Blue Ruby Ivan Raul Ted 350
Green Ted James Rick Ted 125
Red Ted Phil Ruby Bill 300
And in this worksheet, I want to find any instance of the name Bill and get the average of the number found in column F. In this case, the answer of 275 because Bill's name shows up in two rows. In the same respect, If I choose to look at Ted's numbers, the answer should be 258 because Ted's name shows up in three rows.
I would also appreciate if the formula would ignore any blank cells in the process of calculating the answer.
Thanks in advance!
I would use the function below, assuming that the data is placed in Sheet1.
Function my_average(strName As String) As Variant
Dim varArrayNames As Variant
Dim varValues As Variant
Dim dblInSum(1 To 4) As Double '~~> change to "1 To 40"
Dim lngCnt As Long
Dim strRow As String
Dim dblSum As Double
varArrayNames = Sheet1.Range("B1:E4").Value '~~> change to "B1:G40"
varValues = Sheet1.Range("F1:F4").Value '~~> change to "H1:H40"
For lngCnt = LBound(varArrayNames, 1) To UBound(varArrayNames, 1)
strRow = Join(WorksheetFunction.Index(varArrayNames, lngCnt, 0))
If InStr(strRow, strName) > 0 Then
dblInSum(lngCnt) = 1
End If
Next lngCnt
dblSum = WorksheetFunction.Sum(dblInSum)
If dblSum > 0 Then
my_average = WorksheetFunction.SumProduct(dblInSum, Application.Transpose(varValues)) / dblSum
Else
my_average = 0
End If
End Function
Testing:
Place =my_average("Bill") in any workbook (or a cell reference instead of "Bill").
Formulas:
Results:
Assuming the lookup value (Bill etc) is in cell C7, add the following formula in G1 then copy down for other rows.
=IF(ISERROR(MATCH($C$7,A1:E1,0)),"",F1)
Then do
=AVERAGE(G1:G4)
So if Bill is in any col a-e that number is taking into the ave... If so depending on your data size why not do this simply...:
Sub simplesearch()
cnt = 0
tot = 0
srchval = InputBox("What are we looking for?")
lr = Range("A1000000").End(xlUp).Row
For i = 1 To lr
For j = 1 To 5
If Cells(i, j).Value = srchval Then
tot = tot + Cells(i, 6).Value
cnt = cnt + 1
End If
Next j
Next i
If Not (cnt = 0) Then
MsgBox (tot / cnt)
Else
MsgBox ("0")
End If
End Sub
I am having trouble determining a way to enter a 1 or 0 into an adjacent cell to indicate whether or not a value is unique when working with a large dataset. I have read of multiple methods for accomplishing this, however none of them seem efficient for my purposes: I am using an instance of Excel 2010 (so I do not have the Distinct Count feature in PivotTables, and when I try to use PowerPivot it crashes my file due to processing limitations.
In this StackOverflow question: Simple Pivot Table to Count Unique Values there are suggestions to use SUMPRODUCT or COUNTIF, but when working with 50,000+ rows as I am, this causes terrible performance and a file size of ~35 MB instead of ~3 MB. I wanted to know if there is a better solution for a large, dynamic dataset whether it is a formula or VBA.
An example of what I would like to accomplish is (with the Unique column being the adjacent cell):
Name Week Unique
John 1 1
Sally 1 1
John 1 0
Sally 2 1
I attempted to script the same functionality of COUNTIF but with no success:
For Each Cell In ThisWorkbook.Worksheets("Overtime & Type Data").Range("Z2:Z" & DataLastRow)
If Worksheets("Overtime & Type Data").Cells(Cell.Row, 26) <> Worksheets("Overtime & Type Data").Cells(Cell.Row - 1, 26) Then
FirstCell = Cell.Row
End If
If (Worksheets("Overtime & Type Data").Range(Cells(FirstCell, 26), Cells(Cell.Row, 26)) = Worksheets("Overtime & Type Data").Range(Cells(Cell.Row, 26))) = True Then
Cell.Value = 1
Else
Cell.Value = 0
End If
Next Cell
This code ran on over 130,000 rows successfully in less than 3 seconds. Adjust the column letters to fit your dataset.
Sub tgr()
Const colName As String = "A"
Const colWeek As String = "B"
Const colOutput As String = "C"
Dim ws As Worksheet
Dim rngData As Range
Dim DataCell As Range
Dim rngFound As Range
Dim collUniques As Collection
Dim arrResults() As Long
Dim ResultIndex As Long
Dim UnqCount As Long
Set ws = ThisWorkbook.Sheets("Overtime & Type Data")
Set rngData = ws.Range(colName & 2, ws.Cells(Rows.Count, colName).End(xlUp))
Set collUniques = New Collection
ReDim arrResults(1 To rngData.Cells.Count, 1 To 1)
On Error Resume Next
For Each DataCell In rngData.Cells
ResultIndex = ResultIndex + 1
collUniques.Add ws.Cells(DataCell.Row, colName).Value & ws.Cells(DataCell.Row, colWeek).Value, ws.Cells(DataCell.Row, colName).Value & ws.Cells(DataCell.Row, colWeek).Value
If collUniques.Count > UnqCount Then
UnqCount = collUniques.Count
arrResults(ResultIndex, 1) = 1
Else
arrResults(ResultIndex, 1) = 0
End If
Next DataCell
On Error GoTo 0
ws.Cells(rngData.Row, colOutput).Resize(rngData.Cells.Count).Value = arrResults
End Sub
One approach is to sort by Name and Week. Then you can determine Unique for any row by comparing with the previous row.
If you need to preserve the order, you could first write a column of Index numbers (1, 2, 3, ...) to keep track of order. After calculating Unique, sort by Index to restore the original order.
The whole process could be done manually with relatively few steps, or automated with VBA.
I'm not sure how well this will work with 50000 values, but it goes through ~1500 in about a second.
Sub unique()
Dim myColl As New Collection
Dim isDup As Boolean
Dim myValue As String
Dim r As Long
On Error GoTo DuplicateValue
For r = 1 To Sheet1.UsedRange.Rows.Count
isDup = False
'Combine the value of the 2 cells together
' and add that string to our collection
'If it is already in the collection it errors
myValue = Sheet1.Cells(r, 1).Value & Sheet1.Cells(r, 2).Value
myColl.Add r, myValue
If isDup Then
Sheet1.Cells(r, 3).Value = "0"
Else
Sheet1.Cells(r, 3).Value = "1"
End If
Next
On Error GoTo 0
Exit Sub
DuplicateValue:
'The value is already in the collection so put a 0
isDup = True
Resume Next
End Sub
Just about any bulk operation will beat a loop involving worksheet cells. You might be able to trim the time down a bit by performing all of the calculations in memory and only returning the values back to the worksheet en masse when it is complete.
Sub is_a_dupe()
Dim v As Long, vTMP As Variant, vUNQs As Variant, dUNQs As Object
Debug.Print Timer
On Error GoTo bm_Uh_Oh
Set dUNQs = CreateObject("Scripting.Dictionary")
With Worksheets("Sheet1")
vTMP = .Range(.Cells(2, 1), .Cells(Rows.Count, 2).End(xlUp)).Value2
ReDim vUNQs(1 To UBound(vTMP, 1), 1 To 1)
For v = LBound(vTMP, 1) To UBound(vTMP, 1)
If dUNQs.Exists(Join(Array(vTMP(v, 1), vTMP(v, 2)))) Then
vUNQs(v, 1) = 0
Else
dUNQs.Add Key:=Join(Array(vTMP(v, 1), vTMP(v, 2))), _
Item:=vTMP(v, 2)
vUNQs(v, 1) = 1
End If
Next v
.Cells(2, 3).Resize(UBound(vUNQs, 1), 1) = vUNQs
End With
Debug.Print Timer
bm_Uh_Oh:
dUNQs.RemoveAll
Set dUNQs = Nothing
End Sub
Previous experience tells me that the variety of data (as well as hardware, etc) will impact timing the process but in my random sample data I received these elapsed times.
50K records ..... 0.53 seconds
130K records .... 1.32 seconds
500K records .... 4.92 seconds
How do I have to change the following macro code and formula which works for the data and question below(earlier data/question,column F) to make it suitable for Problemstatement (2) and (3) ?
(1)
Earlier data
Colums C,D,E,F
Row 1 4,10,40,F
Row 2 4,12,48,F
Row 3 4,14,56,F
Row 4 3,16,48,F
Row 5 1,18,18,F
Row 6 1,20,10,F
Row 7 0,22,0,0
Intention of the column F
If Cx <> 0, Fx = Cx
If Cx = 0, Fx = the address of the cell in Column C that produces minimum of (C1 * D7 - E1, C2 * D7 - E2, ..., CN * D7 - EN) and is >0.
** Macro code and formula for column F**
Public Function MinimumC()
Dim rngCurrent As Range
Set rngCurrent = Application.ThisCell
Dim rngMin As Range
Dim minimum As Long
minimum = 100000000
Dim tmp As Long
Dim rngC As Range
Set rngC = ActiveSheet.Range("C1:C" & rngCurrent.Row - 1)
For Each c In rngC.Cells
If c.Value2 <> 0 Then
tmp = c.Value2 * rngCurrent.Offset(0, -2).Value2 - c.Offset(0, 2)
If tmp < minimum Then
minimum = tmp
Set rngMin = c
End If
End If
Next c
MinimumC = rngMin.Value2
End Function
Formula in F1 and copy down column F: =IF(C1<>0,C1,MinimumC())
(2)
How do I have to change the macro and formula to archieve the same in the following data format:
New data 1
Colums AZ,BA,BB,BC,BD,BE,BF,BG
Row 1 4,4,4,10,10,10,120,444
Row 2 4,4,4,12,12,12,144,444
Row 3 4,4,4,14,14,14,168,444
Row 4 3,3,3,16,16,16,144,333
Row 5 1,1,1,18,18,18,54,111
Row 6 1,1,1,20,20,20,60,111
Row 7 0,0,0,22,22,22,0,???
Intention of the column BG
If(And(AZ>0;BA>0;BB>0);Concatenate(AZ;BA;BB))
otherwise, the adress cell of Concatenate (AZ;BA;BB), unequal to 000, to minimize the following difference (AZn*BCx+BAn*BDx+BBn*BEx)-BFn
(3)
How do I have to change the macro and formula from (1) to archieve the same in the following data format:
New data 2
Colums AZ,BA,BB,BC,BD,BE,BF,BG,BH,BI,BJ,BK,BL,BM,BN
Row 1 4,4,4,10,10,10,120,444,3,3,3,10,10,10,90,333
Row 2 4,4,4,12,12,12,144,444,3,3,3,12,12,12,108,333
Row 3 4,4,4,14,14,14,168,444,3,3,3,14,14,14,126,333
Row 4 3,3,3,16,16,16,144,333,3,3,3,16,16,16,144,333
Row 5 1,1,1,18,18,18,54,111,2,2,2,18,18,18,108,222
Row 6 1,1,1,20,20,20,60,111,1,1,1,20,20,20,60,111
Row 7 0,0,0,22,22,22,0,?,0,0,0,22,22,22,0,?
Intention of the column BN
If(And(AZ>0;BA>0;BB>0;BH>0;BI>0;BJ>0);0
otherwise the adress of the cell either concatenate (AZ;BA;BB) or concatenate (BH;BI;BJ) to minimize (AZn*BCx+BAn*BDx+BBn*BEx)-BFn or (BHn*BKx+BIn*BLx+BJn*BMx)-BNn .In this case i need to find the adress of either concatenate (AZ;BA;BB) or concatenate (BH;BI;BJ) with the min difference and that cell of BN (concatenate) unequal to 000.
I appriciate the help. Thank you very much!!!!
To solve problem 2, the macro is basically identical. You just need to replace the C range with the AZ range, and change the calculation of tmp:
Public Function CalcBG()
Dim rngCurrent As Range
Set rngCurrent = Application.ThisCell
Debug.Print rngCurrent.Address
Dim rngMin As Range
Dim minimum As Long
minimum = 100000000
Dim tmp As Long
Dim rngAZ As Range
Set rngAZ = ActiveSheet.Range("AZ1:AZ" & rngCurrent.Row - 1)
Debug.Print rngAZ.Address
For Each c In rngAZ.Cells
If c.Value2 <> 0 Then
tmp = ((c.Value * rngCurrent.Offset(0, -4).Value2) + (c.Offset(0, 1).Value2 * rngCurrent.Offset(0, -3).Value2) + (c.Offset(0, 2).Value2 * rngCurrent.Offset(0, -2)) - c.Offset(0, 6).Value2) 'This is your calculation (AZn * BCx) + ... - BFn
If tmp < minimum Then
minimum = tmp
Debug.Print minimum
Set rngMin = c.Offset(0, 7)
Debug.Print rngMin
End If
End If
Next c
Debug.Print minimum
Debug.Print rngMin.Address
CalcBG = rngMin.Address 'Return the address rather than the value
End Function
And the formula:
=IF(AND(AZ1>0,BA1>0,BB1>0),CONCATENATE(AZ1,BA1,BB1),CalcBG())
Place that in BG1 and copy down Column BG.
Problem 3
This added a couple more variables, but the basic structure is the same.
Based on the data, I also assumed that "If(And(AZ>0;BA>0;BB>0;BH>0;BI>0;BJ>0);0" should have been If(And(AZ>0;BA>0;BB>0;BH>0;BI>0;BJ>0),concatenate(bh,bi,bj). Otherwise your values in BO would all be 0. Also, your last column is BO, not BN. :)
Here's the macro:
Public Function CalcBO()
Dim rngCurrent As Range
Set rngCurrent = Application.ThisCell
Debug.Print rngCurrent.Address
Dim rngMin As Range
Dim minimum As Long
minimum = 100000000
Dim tmp1 As Long
Dim tmp2 As Long
Dim lowest As Long
Dim rngAZ As Range
Set rngAZ = ActiveSheet.Range("AZ1:AZ" & rngCurrent.Row - 1)
Debug.Print rngAZ.Address
For Each c In rngAZ.Cells
If c.Value2 <> 0 Then
'(AZn*BCx+BAn*BDx+BBn*BEx)-BFn
tmp1 = ((c.Value2 * rngCurrent.Offset(0, -12).Value2) + (c.Offset(0, 1).Value2 * rngCurrent.Offset(0, -13).Value2) + (c.Offset(0, 2).Value2 * rngCurrent.Offset(0, -10).Value2)) - c.Offset(0, 6).Value2
'(BHn*BKx+BIn*BLx+BJn*BMx)-BNn
tmp2 = ((c.Offset(0, 8).Value2 * rngCurrent.Offset(0, -4).Value2) + (c.Offset(0, 9).Value2 * rngCurrent.Offset(0, -3).Value2) + (c.Offset(0, 10).Value2 * rngCurrent.Offset(0, -2).Value2)) - c.Offset(0, 14).Value2
lowest = WorksheetFunction.Min(tmp1, tmp2)
If lowest < minimum Then
minimum = lowest
Debug.Print minimum
Set rngMin = c.Offset(0, 7)
Debug.Print rngMin
End If
End If
Next c
Debug.Print minimum
Debug.Print rngMin.Address
CalcBO = rngMin.Address
End Function
And here is the formula for BO1. Copy down as usual:
=IF(AND(AZ1>0,BA1>0,BB1>0,BH1>0,BI1>0,BJ1>0),CONCATENATE(BH1,BI1,BJ1),CalcBO())
There's a lot of repetition among those macros, and they could probably be condensed. But they should do what you need.
One note: The macros won't differentiate between equal minimum values. For example, in problem set 3, the two calculations (AZn ... - BFn and BHn ... - BNn) return the same value, 6, which also happens to be the minimum value among all the calculations. In this case, it returns the first address (AZn ... - BFn). So in Problem 3, you will get the answer $BG$6 in BO7, even though $BG$6 and $BO$6 both satisfy the conditions. Your requirements didn't specify what to do in case there was more than one minimum value, so I left it as it is.
Let me know if you need any help with the above.