Inefficient excel code breaks after few thousand lines of data - excel
I am fairly new to Excel and VBA. I wrote a code that separates a line of data into multiple sections which then adds headers, colors, and plots.
The problem is when I have many lines of data. My code runs just fine when I have about 4000 lines of data, but I get say about 10000 lines, Excel freezes and does not respond anymore. The code is fairly long and I do expect anyone to read the entire thing.
My doubts are that excel does not respond and crashes because there is a watch-dog timer that times the execution of the code and if it does not receive anything back then it crashes. This is only a guess.
Here is a few lines of the actual data that I need to filter and everything.
2017:06:29T14:12:11,0,1013,00,156,-0.112,12.751,000,000,38,34,33,1014,00,202,-0.102,12.734,000,000,38,35,33,1015,00,174,-0.105,12.755,000,000,37,35,33,1008,00,156,-0.110,12.741,000,000,37,35,33,
2017:06:29T14:12:12,0,1013,00,157,-0.102,12.758,000,000,38,34,33,1014,00,203,-0.105,12.744,000,000,38,35,33,1015,00,175,-0.103,12.757,000,000,37,35,33,1008,00,157,-0.107,12.757,000,000,37,35,33,
2017:06:29T14:12:13,0,1013,00,158,-0.113,12.737,000,000,38,34,33,1014,00,204,-0.094,12.760,000,000,38,35,33,1015,00,176,-0.117,12.748,000,000,37,35,33,1008,00,158,-0.109,12.744,000,000,37,35,33,
2017:06:29T14:12:14,0,1013,00,159,-0.103,12.753,000,000,38,34,33,1014,00,205,-0.103,12.720,000,000,38,35,33,1015,00,177,-0.108,12.732,000,000,37,35,33,1008,00,159,-0.110,12.758,000,000,37,35,33,
2017:06:29T14:12:15,0,1013,00,160,-0.112,12.757,000,000,38,34,33,1014,00,206,-0.095,12.734,000,000,38,35,33,1015,00,178,-0.118,12.729,000,000,37,35,33,1008,00,160,-0.115,12.755,000,000,37,35,33,
I am open to any suggestions and more than happy to learn. Thank you for your time and help in advance.
Sub SeparateData()
'Author: Me
'Date: July 13, 2017
'Purpose: This macro take the data in the worksheet and separates the data in a readable fashion for the user.
' This macro also plots and reports any errors that it has caught both in separate sheets named accordingly.
'Define variables
Dim i As Variant
Dim j As Variant
Dim k As Variant
Dim data As Variant
Dim data2 As Variant
Dim count As Variant
Dim shiftDown As Variant
Dim monitorNum As Variant
Dim errorCount As Variant
Dim battChart As ChartObject
Dim currChart As ChartObject
Dim tempChart As ChartObject
'Stop the alerts so we can erase the sheets peacefully
Application.DisplayAlerts = False
'Erase the extra sheets
Sheets("Sheet2").Delete
Sheets("Sheet3").Delete
'Turn on the alerts in case something else happened
Application.DisplayAlerts = True
'Rename the first sheet
ActiveSheet.Name = "Data"
'Create a new sheet for the plots
Sheets.Add.Name = "Plots"
'Create a new sheet for the errors
Sheets.Add.Name = "Errors"
'Activate the first sheet for data processing
Worksheets("Data").Activate
'Enter the number of monitors
monitorNum = 4
'Variable to shift down the data so that te headers will fit (recommended 2)
shiftDown = 2
'Variable to count the number of errors the program thinks occured
errorCount = 0
'Count how many data point there are in the sheet
count = Cells(1, 1).CurrentRegion.Rows.count
'Iterate through the points separating the Data
For i = 0 To count - 1
'First separate the date from the rest
data = Cells(count - i, 1).Value
data = Split(data, "T")
For j = 0 To UBound(data)
Cells(count - i + shiftDown, j + 1).Value = data(j)
Next j
'Now separate the rest of the data
data2 = data(1)
data2 = Split(data2, ",")
For j = 0 To UBound(data2)
Cells(count - i + shiftDown, j + 2).Value = data2(j)
Next j
For k = 0 To monitorNum - 1
'Check for voltage error
If Cells(count - i + shiftDown, (k * 10) + 8).Value > 20 Or IsNumeric(Cells(count - i + shiftDown, (k * 10) + 8).Value) = False Then
'increment the number of errors found
errorCount = errorCount + 1
'Activate the Errors sheet for error recording
Worksheets("Errors").Activate
'Save the row number and the monitor number where the error was founf
Cells(errorCount, 1).Value = "Voltage error in row"
Cells(errorCount, 2).Value = count - i + shiftDown
Cells(errorCount, 3).Value = "in column"
Cells(errorCount, 4).Value = (k * 10) + 8
Cells(errorCount, 5).Value = "in Monitor"
Cells(errorCount, 6).Value = k + 1
Cells(errorCount, 7).Value = "The recorded data was"
Sheets("Data").Cells(count - i + shiftDown, (k * 10) + 8).Copy Cells(errorCount, 8)
'Autofit all the columns
Cells(1, 1).CurrentRegion.EntireColumn.AutoFit
'Activate the first sheet for data processing
Worksheets("Data").Activate
'Clear the contents of the error
Cells(count - i + shiftDown, (k * 10) + 8).ClearContents
End If
'Check for current error
If Cells(count - i + shiftDown, (k * 10) + 7).Value > 80 Or IsNumeric(Cells(count - i + shiftDown, (k * 10) + 7).Value) = False Then
'increment the number of errors found
errorCount = errorCount + 1
'Activate the Errors sheet for error recording
Worksheets("Errors").Activate
'Save the row number and the monitor number where the error was founf
Cells(errorCount, 1).Value = "Current error in row"
Cells(errorCount, 2).Value = count - i + shiftDown
Cells(errorCount, 3).Value = "in column"
Cells(errorCount, 4).Value = (k * 10) + 7
Cells(errorCount, 5).Value = "in Monitor"
Cells(errorCount, 6).Value = k + 1
Cells(errorCount, 7).Value = "The recorded data was"
Sheets("Data").Cells(count - i + shiftDown, (k * 10) + 7).Copy Cells(errorCount, 8)
'Autofit all the columns
Cells(1, 1).CurrentRegion.EntireColumn.AutoFit
'Activate the first sheet for data processing
Worksheets("Data").Activate
'Clear the contents of the error
Cells(count - i + shiftDown, (k * 10) + 7).ClearContents
End If
'Check for temperature error
If Cells(count - i + shiftDown, (k * 10) + 13).Value > 80 Or IsNumeric(Cells(count - i + shiftDown, (k * 10) + 13).Value) = False Then
'increment the number of errors found
errorCount = errorCount + 1
'Activate the Errors sheet for error recording
Worksheets("Errors").Activate
'Save the row number and the monitor number where the error was founf
Cells(errorCount, 1).Value = "Temperature error in row"
Cells(errorCount, 2).Value = count - i + shiftDown
Cells(errorCount, 3).Value = "in column"
Cells(errorCount, 4).Value = (k * 10) + 13
Cells(errorCount, 5).Value = "in Monitor"
Cells(errorCount, 6).Value = k + 1
Cells(errorCount, 7).Value = "The recorded data was"
Sheets("Data").Cells(count - i + shiftDown, (k * 10) + 13).Copy Cells(errorCount, 8)
'Autofit all the columns
Cells(1, 1).CurrentRegion.EntireColumn.AutoFit
'Activate the first sheet for data processing
Worksheets("Data").Activate
'Clear the contents of the error
Cells(count - i + shiftDown, (k * 10) + 13).ClearContents
End If
Next k
Next i
'Erase the data that has been duplicated
For i = 1 To shiftDown
Cells(i, 1).Value = ""
Next i
'Write and color the headers
'For the Date
Range(Cells(shiftDown - 1, 1), Cells(shiftDown, 1)).Merge
Range(Cells(shiftDown - 1, 1), Cells(shiftDown, 1)).Value = "Date"
Range(Cells(shiftDown - 1, 1), Cells(count + shiftDown, 1)).Interior.Color = RGB(200, 190, 150)
'For the Time
Range(Cells(shiftDown - 1, 2), Cells(shiftDown, 2)).Merge
Range(Cells(shiftDown - 1, 2), Cells(shiftDown, 2)).Value = "Time"
Range(Cells(shiftDown - 1, 2), Cells(count + shiftDown, 2)).Interior.Color = RGB(150, 140, 80)
'For the Key Switch
Range(Cells(shiftDown - 1, 3), Cells(shiftDown, 3)).Merge
Range(Cells(shiftDown - 1, 3), Cells(shiftDown, 3)).Value = "Key Switch"
Range(Cells(shiftDown - 1, 3), Cells(count + shiftDown, 3)).Interior.Color = RGB(200, 200, 0)
For i = 1 To monitorNum
Range(Cells(shiftDown - 1, ((i - 1) * 10) + 4), Cells(shiftDown - 1, (i * 10) + 3)).Merge
Range(Cells(shiftDown - 1, ((i - 1) * 10) + 4), Cells(shiftDown - 1, (i * 10) + 3)).Value = "Monitor " & i
'color the headers
If i Mod 4 = 0 Then
Range(Cells(shiftDown - 1, ((i - 1) * 10) + 4), Cells(shiftDown - 1, (i * 10) + 3)).Interior.Color = RGB(100, 255, 100)
ElseIf i Mod 3 = 0 Then
Range(Cells(shiftDown - 1, ((i - 1) * 10) + 4), Cells(shiftDown - 1, (i * 10) + 3)).Interior.Color = RGB(255, 100, 10)
ElseIf i Mod 2 = 0 Then
Range(Cells(shiftDown - 1, ((i - 1) * 10) + 4), Cells(shiftDown - 1, (i * 10) + 3)).Interior.Color = RGB(100, 100, 255)
Else
Range(Cells(shiftDown - 1, ((i - 1) * 10) + 4), Cells(shiftDown - 1, (i * 10) + 3)).Interior.Color = RGB(255, 75, 75)
End If
Next i
For i = 0 To monitorNum - 1
'Monitor ID
Cells(shiftDown, 1 + (i * 10) + 3).Value = "MONITOR_NUM"
'Monitor status
Cells(shiftDown, 2 + (i * 10) + 3).Value = "MONITOR_STATUS"
'Heart Beat count
Cells(shiftDown, 3 + (i * 10) + 3).Value = "HB_COUNT"
'For Current
Cells(shiftDown, 4 + (i * 10) + 3).Value = "CURRENT"
Range(Cells(shiftDown, 4 + (i * 10) + 3), Cells(count + shiftDown, 4 + (i * 10) + 3)).Interior.Color = RGB(240, 150, 150)
'For Voltage
Cells(shiftDown, 5 + (i * 10) + 3).Value = "VOLTAGE"
Range(Cells(shiftDown, 5 + (i * 10) + 3), Cells(count + shiftDown, 5 + (i * 10) + 3)).Interior.Color = RGB(110, 160, 180)
'State of Charge
Cells(shiftDown, 6 + (i * 10) + 3).Value = "SOC"
'State of Health
Cells(shiftDown, 7 + (i * 10) + 3).Value = "SOH"
'Chip temperature
Cells(shiftDown, 8 + (i * 10) + 3).Value = "TEMP_CHP"
'Internal temperature
Cells(shiftDown, 9 + (i * 10) + 3).Value = "TEMP_INT"
'For Temperature of the terminal
Cells(shiftDown, 10 + (i * 10) + 3).Value = "TEMP_EXT"
Range(Cells(shiftDown, 10 + (i * 10) + 3), Cells(count + shiftDown, 10 + (i * 10) + 3)).Interior.Color = RGB(255, 190, 0)
Next i
'Add borders all around the data
Cells(shiftDown, 1).CurrentRegion.Borders.LineStyle = xlContinuous
'Autofit all the columns
Cells(shiftDown, 1).CurrentRegion.EntireColumn.AutoFit
'Plotting
'Activate the first sheet for data plotting
Worksheets("Data").Activate
'Add a new plot
Set battChart = Sheets("Plots").ChartObjects.Add(0, 0, 1200, 300)
'Plot the battery data
With battChart.Chart
.SetSourceData Source:=Sheets("Data").Range(Cells(5, 8), Cells(count + shiftDown, 8))
.SeriesCollection(1).Name = "Battery 1"
.ChartWizard Title:="Voltage", HasLegend:=True, CategoryTitle:="Time (s)", ValueTitle:="Voltage (V)", Gallery:=xlXYScatterLinesNoMarkers
For i = 2 To monitorNum
.SeriesCollection.NewSeries
.SeriesCollection(i).Values = Sheets("Data").Range(Cells(5, ((i - 1) * 10) + 8), Cells(count + shiftDown, ((i - 1) * 10) + 8))
.SeriesCollection(i).Name = "Battery " & i
Next i
End With
'Add a new plot
Set currChart = Sheets("Plots").ChartObjects.Add(0, 300, 1200, 300)
'Plot the current data
With currChart.Chart
.SetSourceData Source:=Sheets("Data").Range(Cells(5, 7), Cells(count + shiftDown, 7))
.SeriesCollection(1).Name = "Battery 1"
.ChartWizard Title:="Current", HasLegend:=True, CategoryTitle:="Time (s)", ValueTitle:="Current (A)", Gallery:=xlXYScatterLinesNoMarkers
For i = 2 To monitorNum
.SeriesCollection.NewSeries
.SeriesCollection(i).Values = Sheets("Data").Range(Cells(5, ((i - 1) * 10) + 7), Cells(count + shiftDown, ((i - 1) * 10) + 7))
.SeriesCollection(i).Name = "Battery " & i
Next i
End With
'Add a new plot
Set tempChart = Sheets("Plots").ChartObjects.Add(0, 600, 1200, 300)
'Plot the current data
With tempChart.Chart
.SetSourceData Source:=Sheets("Data").Range(Cells(5, 13), Cells(count + shiftDown, 13))
.SeriesCollection(1).Name = "Battery 1"
.ChartWizard Title:="Temperature", HasLegend:=True, CategoryTitle:="Time (s)", ValueTitle:="Temperature (F)", Gallery:=xlXYScatterLinesNoMarkers
For i = 2 To monitorNum
.SeriesCollection.NewSeries
.SeriesCollection(i).Values = Sheets("Data").Range(Cells(5, ((i - 1) * 10) + 13), Cells(count + shiftDown, ((i - 1) * 10) + 13))
.SeriesCollection(i).Name = "Battery " & i
Next i
End With
'Indicate that the macro has finished its job
Beep
MsgBox "Data separation is complete. There were " & errorCount & " errors found."
End Sub
All of your Worksheets("x").Activate are totally unnecessary, are slowing your code significantly and are begging for inexplicable errors later when you forget to activate the correct sheet or your bored user starts clicking around during execution because it's taking too long. Declare some Worksheet variables and work with those.
Dim DataSheet as Worksheet
ActiveSheet.Name = "Data"
Set DataSheet = ActiveSheet
Dim PlotSheet as Worksheet
Set PlotSheet as Worksheets.Add
Plotsheet.Name = "Plots"
Dim ErrorSheet as Worksheet
Set ErrorSheet = Worksheets.Add
ErrorSheet.Name = "Errors"
count = Datasheet.Cells(1, 1).CurrentRegion.Rows.count
'GET RID OF THIS EVERYWHERE!!! Worksheets("Errors").Activate
'Save the row number and the monitor number where the error was founf
With ErrorSheet
.Cells(errorCount, 1).Value = "Voltage error in row"
.Cells(errorCount, 2).Value = count - i + shiftDown
.Cells(errorCount, 3).Value = "in column"
.Cells(errorCount, 4).Value = (k * 10) + 8
.Cells(errorCount, 5).Value = "in Monitor"
.Cells(errorCount, 6).Value = k + 1
.Cells(errorCount, 7).Value = "The recorded data was"
'Note subtle change here:
DataSheet.Cells(count - i + shiftDown, (k * 10) + 8).Copy .Cells(errorCount, 8)
'Note: explicitly setting "datasheet" as the destination and using the "With" to save some typing on the ".Cells" call.
'You could explicitly type the "ErrorSheet" to make it more clear
'an even better version is:
.cells(errorCount, 8) = DataSheet.Cells(count - i + shiftDown, (k * 10) + 8)
End With
Continue to do that everywhere. Future you will appreciate current you...
Every time you're doing a Sheet("x").Activate eliminate that line and explicitly add a reference to the appropriate worksheet variable you declared earlier.
Every time you have an unqualified Sheets or Cells or Range call, make it an explicit reference by prepending the appropriate worksheet variable. Future you will appreciate the fact that you can see exactly what worksheet you're referencing. Sure, there may be some extra typing involved, but that extra typing significantly reduces the chance of inserting very subtle and difficult to find bugs.
Using .Copy is very slow for single cells. It does gain a speed advantage if you're copying large blocks of cells in a single go (somewhere in the neighborhood of 3-5k cells in a single copy statement vs a loop through setting individual cell values).
As Uri Goren pointed out setting Application.Calculation = False will definitely improve your speed. I'd recommend not setting Application.ScreenUpdating = False until after your code is 100% functional and not generating any errors. Once you're at that point, it's a great thing to do.
At this point in your code you may want to add the indicated line:
'Iterate through the points separating the Data
For i = 0 To count - 1
'Add this line:
Application.StatusBar = "Separating points #" & i
Put a similar message just inside the top of each of your large loops. You'll likely see that your code isn't hanging, just taking a long time to process. Plus you'll have an update that your user can watch so (s)he'll know it isn't hung and is still doing something.
At the end of your code put:
Application.StatusBar = ""
To clear the message so you get your normal Excel StatusBar functionality returned.
Add these two lines at the beginning of the subroutine:
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
And these 2 lines before the end of the subroutine
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
It should speed up your code significantly
Related
Decode excel date mask
I need to try to find an algorithm to decode a set of Excel dates. A former employee once told me he could encode a date in Excel and no one would ever figure it out, he said "like a needle in a haystack". My response was "Why would someone want to do that?" After his departure with the theft of a large amount of data, I find a set of charts with "record numbers" labeled anywhere from from T20000 to T39999. These charts should have accompanying birthdates, but do not - the birthdates are missing. These charts were hidden in our usual charts which are 6 to 7 digits and pure numeric only rather than starting with a "T". Given this background(and other data), I doubt this was done for innocent reasons and suspect fraud, such as Medicare or insurance fraud. The 5 digits after the "T" could encode the birthdates. These are similar to the usual 5 digit numbers Excel uses to store dates. I was able to find some birthdates by finding other ways to look them up. A few examples are: Record # Exel DOB (real) DOB T30007 27369 12/6/1974 T31414 16803 1/1/1946 T31738 22156 8/28/1960 There are 2-3 K of these without birthdates. Simple addition or subtraction does not find the answer. I suspect he added(or subtracted) then flipped the individual digits, then added(or subtracted) again or a similar algorithm. Changing the order of the 5 digits like 5 cards in poker(The former employee played a lot of poker). I wrote an Excel VBA program to try to evaluate this using trials of: 1) adding 1 to 9999, 2) then switching the order of the last 4 digits, then 3) adding or subtracting 1 to 9999. However, the number of possibilities (9999 * 2^4 * 9999 * 2 ) far exceeds the row limits in Excel(with non matches excluded) and breaking it into smaller parts still ran too slowly on my non gaming computers. I am not well versed in C, Java or Python, but suspect these may be better options. My slow running Excel VBA code: Sub Decode_DOB() 'The accompanying table is Sheet1 which this runs off Dim Org(111, 5) As Long Dim BD1(111, 5) As Long Dim Var_1(111, 5) As Long Dim VarTT(111) As Long Dim VarTT1(111) As Long Dim VarTT2(111) As Long Dim Save_Counter As Long Dim icounter As Long Dim int_Digit_Count As Integer Save_Counter = 1 'Counter to save the spreadsheet at intervals icounter = 2 'Counter to keep track of current row number 'Org(x,y) array to store the original chart record starting with "T" 'Org(n2,0) for the total 5 digit number, then Org(n2,1 to 5) to break out each digit For n2 = 2 To 25 Org(n2, 0) = Sheet1.Cells(n2, 2).Value Org(n2, 1) = Mid(Org(n2, 0), 1, 1) Org(n2, 2) = Mid(Org(n2, 0), 2, 1) Org(n2, 3) = Mid(Org(n2, 0), 3, 1) Org(n2, 4) = Mid(Org(n2, 0), 4, 1) Org(n2, 5) = Mid(Org(n2, 0), 5, 1) Next n2 'BD1(x,y) array to store the original chart record starting with "T" 'BD1(n1,0) for the total 5 digit number, then BD1(n1,1 to 5) to break out each digit For n1 = 2 To 25 BD1(n1, 0) = Sheet1.Cells(n1, 28).Value BD1(n1, 1) = Mid(BD1(n1, 0), 1, 1) BD1(n1, 2) = Mid(BD1(n1, 0), 2, 1) BD1(n1, 3) = Mid(BD1(n1, 0), 3, 1) BD1(n1, 4) = Mid(BD1(n1, 0), 4, 1) BD1(n1, 5) = Mid(BD1(n1, 0), 5, 1) Next n1 'Sequentially try adding 1 to 9999 For n = 1 To 9999 For i = 2 To 25 Var_1(i, 0) = Org(i, 0) - n Var_1(i, 1) = Mid(Var_1(i, 0), 1, 1) Var_1(i, 2) = Mid(Var_1(i, 0), 2, 1) Var_1(i, 3) = Mid(Var_1(i, 0), 3, 1) Var_1(i, 4) = Mid(Var_1(i, 0), 4, 1) Var_1(i, 5) = Mid(Var_1(i, 0), 5, 1) 'Sequentially "Flip the digits" or "Cards" only using the last 4 For k = 2 To 5 For l = 2 To 5 For m = 2 To 5 For p = 2 To 5 'Combine digits to get the total 5 digit number VarTT(i) = Var_1(i, 1) * 10000 + Var_1(i, k) * 1000 + Var_1(i, l) * 100 + Var_1(i, m) * 10 + Var_1(i, p) 'Sequentially and and subtract from the result For ii = 1 To 9999 VarTT1(i) = VarTT(i) + ii VarTT2(i) = VarTT(i) - ii 'Test valid "Flips" - only one of each digit allowed 'and the total 1+2+3+4+5 should = 15 int_Digit_Count = 1 + k + l + m + p If (k = l Or k = m) Or k = p Then int_Digit_Count = 0 End If If l = m Or l = p Then int_Digit_Count = 0 End If If m = p Then int_Digit_Count = 0 End If 'test to see if the result matches the birthdate If VarTT1(i) = BD1(i, 0) Or VarTT2(i) = BD1(i, 0) Then 'and test for valid "Flip" If int_Digit_Count = 15 Then 'If the sequence and valid flip creates a match, write to Sheet2 Sheet2.Cells(icounter, 1).Value = i Sheet2.Cells(icounter, 2).Value = Org(i, 0) Sheet2.Cells(icounter, 3).Value = Org(i, 0) - n Sheet2.Cells(icounter, 4).Value = VarTT(i) Sheet2.Cells(icounter, 5).Value = BD1(i, 0) Sheet2.Cells(icounter, 6).Value = n Sheet2.Cells(icounter, 7).Value = Var_1(i, 1) Sheet2.Cells(icounter, 8).Value = Var_1(i, 2) Sheet2.Cells(icounter, 9).Value = Var_1(i, 3) Sheet2.Cells(icounter, 10).Value = Var_1(i, 4) Sheet2.Cells(icounter, 11).Value = Var_1(i, 5) Sheet2.Cells(icounter, 13).Value = 1 Sheet2.Cells(icounter, 14).Value = k Sheet2.Cells(icounter, 15).Value = l Sheet2.Cells(icounter, 16).Value = m Sheet2.Cells(icounter, 17).Value = p Sheet2.Cells(icounter, 19).Value = VarTT1(i) Sheet2.Cells(icounter, 20).Value = VarTT2(i) Sheet2.Cells(icounter, 21).Value = ii FlipPattern = 1 * 10000 + k * 1000 + l * 100 + m * 10 + p Sheet2.Cells(icounter, 22).Value = FlipPattern icounter = icounter + 1 'Save the spreadsheet every 5000 rows in case the program "blows up" Save_Counter = Save_Counter + 1 If Save_Counter > 5000 Then ActiveWorkbook.Save Save_Counter = 1 Sheet2.Cells(1, 12).Value = icounter Sheet2.Cells(icounter, 24).Value = Time() Application.Wait (Now + TimeValue("00:00:01")) End If End If End If 'ii for last number added or subtracted Next ii 'p,m,l,k for flip sequences Next p Next m Next l Next k 'For each "T" number on Sheet 1 Next i Next n 'Matches can then be sorted on n, "Flip" and ii and look for a "common denominator" End Sub Sub Sort_Matches() 'Run this after sorting to mark multiple matches and filter last_row = Sheet5.Cells(Rows.count, 1).End(xlUp).Row For ijj = 2 To last_row If Sheet5.Cells(ijj, 6).Value = Sheet5.Cells(ijj - 1, 6).Value Then If Sheet5.Cells(ijj, 21).Value = Sheet5.Cells(ijj - 1, 21).Value Then If Sheet5.Cells(ijj, 22).Value = Sheet5.Cells(ijj - 1, 22).Value Then Sheet5.Cells(ijj - 1, 23).Value = "XX" Sheet5.Cells(ijj, 23).Value = "XX" End If End If End If Next ijj End Sub
Increment value within range until corresponding cell value increases by specified amount
I have this problem to be sorted out. Namely, I need the code that will place the same values in the range until corresponding cells value increases by one. Once it does the value needs to be incremented by 0.2 and place incremented number until again the corresponding cell value is higher by one Sub Button4_Click() range1 = Sheets(3).Range("g2").End(xlDown).Row range2 = "g2:g" & range1 For i = 2 To range1 If Cells(i, 7).Value <= 360 Then Cells(i, 8) = 60 range3_n = Cells(i, 8).Row End If Next j = 0.2 k = 1 For i = range3_n To range1 If Cells(i, 7) > 360 And Cells(i, 7) <= (360 + k) And Cells(i, 7) <= (360 + 100) Then Cells(i, 8) = 60 + (k * j) k = k + 1 End If Next MsgBox ("END") End Sub
Based on the comments, and keeping the code structure the exact same as it is, an easy solution is adding a new IF Statement after your last one: IF (Cells(i, 7) - Int(Cells(i, 7)) = 0) Then The code should look like: Sub Button4_Click() range1 = Sheets(1).Range("g2").End(xlDown).Row range2 = "g2:g" & range1 For i = 2 To range1 If Cells(i, 7).Value <= 360 Then Cells(i, 8) = 60 range3_n = Cells(i, 8).Row End If Next range3_n = range3_n + 1 'start at cell 361 instead j = 0.2 k = 1 For i = range3_n To range1 If Cells(i, 7) > 360 And Cells(i, 7) <= (361 + k) And Cells(i, 7) <= (360 + 100) Then If Int(Cells(i - 1, 7)) - Int(Cells(i, 7)) <> 0 Then Cells(i, 8) = 60 + (k * j) k = k + 1 Else Cells(i, 8) = Cells(i - 1, 8) End If End If Next i MsgBox ("END") End Sub What this does is, it takes the value of the cell, subtracts the integer version of that value (360.1 = 360, 365.4 = 365, etc.) and ensures that equals zero. Any decimal value > .0 will fail, and will not meet the criteria. Example: 360.0 - 360 = 0 PASS 360.1 - 360 = .1 FAIL 360.4 - 360 = .4 FAIL 360.7 - 360 = .7 FAIL 361.0 - 361 = 0 PASS Try it out and let me know.
Excel VBA - Merging cells based on duplicates search
I need help writing a VBA code to find duplicate values in one column and then merge cells based off that search. E.g: France 6216 EDE 009789 Company A France 6216 EDF 009790 Company A France 6216 EDG 009791 Company A Germany 6216 EDH 009792 Company B Becomes: France 6216 EDE EDF EDG 009789 009790 009791 Company A Germany 6216 EDH 009792 Company B Its on a large spreadsheet where some dupes will have two but some could be as many as eight. Can anyone help me? Any questions please let me know. Thanks so much!
Try out this macro, Sub removeDupes() Dim i As Long, j As Long, k As Long Columns("A:E").Sort key1:=Range("A2"), order1:=xlAscending, Header:=xlYes Sheets.Add.Name = "newSheet" Sheets("newSheet").Cells(1, 1) = Cells(2, 1) Sheets("newSheet").Cells(1, 2) = Cells(2, 2) Sheets("newSheet").Cells(1, 3) = Cells(2, 3) Sheets("newSheet").Cells(1, 150) = Cells(2, 4) Sheets("newSheet").Cells(1, 255) = Cells(2, 5) j = 1 k = 1 For i = 2 To Cells(Rows.Count, 1).End(xlUp).Row If Cells(i + 1, 1) = Cells(i, 1) Then Sheets("newSheet").Cells(j, 3 + k) = Cells(i + 1, 3) Sheets("newSheet").Cells(j, 150 + k) = Cells(i + 1, 4) k = k + 1 Else j = j + 1 Sheets("newSheet").Cells(j, 1) = Cells(i + 1, 1) Sheets("newSheet").Cells(j, 2) = Cells(i + 1, 2) Sheets("newSheet").Cells(j, 3) = Cells(i + 1, 3) Sheets("newSheet").Cells(j, 150) = Cells(i + 1, 4) Sheets("newSheet").Cells(j, 255) = Cells(i + 1, 5) k = 1 End If Next i For i = 255 To 1 Step -1 If Sheets("newSheet").Cells(1, i) = "" Then Sheets("newSheet").Columns(i).Delete End If Next i End Sub Source: Output:
VBA: Application-defined error when refering to range in different tab [duplicate]
This question already has an answer here: Why does Range work, but not Cells? (1 answer) Closed 5 years ago. In this sub, I'm simply doing a few calculations. In a differnt sheet I've some temporary data stored and I'm trying and failing to define the following as a range (at the very bottom at the code): MeanVector = Worksheets("TempSheet").Range(Cells(2, 2), Cells(Stocks + 1, 2)) I get the following error: 1004 application-defined or object-defined error The thing though is, this is a line of code that I copied in from a different module, so I know the method itself works. I assume the problem is with the activation of the sheets, I'm tried to activate both at the beginning but with no luck. I do specify the worksheet in evry line of code, so I'm not sure why it would still be a problem? I've tried to select the range as well: Set MeanVector = Worksheets("TempSheet").Range(Cells(2, 2), Cells(Stocks + 1, 2)) All code: Sub CalculateOwnPortfolio() Application.ScreenUpdating = False Application.DisplayAlerts = False Application.StatusBar = "Calculating..." Dim MeanVector As Range Dim WeightsVector As Range Worksheets("TempSheet").Activate Worksheets("Own Portfolio").Activate 'Count the amount of stock Stocks = 0 For i = 1 To 20 If Worksheets("MainSheet").Cells(i + 2, 2) <> 0 Then Stocks = Stocks + 1 Else Stocks = Stocks End If Next 'Amount of each stock EmptyAmount = 0 For i = 1 To Stocks If Worksheets("Own Portfolio").Cells(1 + i, 7) = Value Then Else EmptyAmount = EmptyAmount + 1 End If Next If EmptyAmount = 0 Then MsgBox ("Error: Enter stock amounts") Exit Sub End If 'Calcualte amount of observations Observations = 0 For j = 2 To 15000 If Worksheets("Own Portfolio").Cells(j, 1) <> 0 Then Observations = Observations + 1 Else Observations = Observations End If Next Worksheets("Own Portfolio").Range(Cells(2, 2), Cells(Observations, 2)) _ .ClearContents Worksheets("Own Portfolio").Range(Cells(2, 5), Cells(3 + Stocks, 5)) _ .ClearContents 'Total Value For i = 2 To Observations + 1 Value = 0 For j = 1 To Stocks Symbol = Worksheets("Own Portfolio").Cells(1 + j, 4) Amount = Worksheets("Own Portfolio").Cells(1 + j, 7) AdjClose = Worksheets(Symbol).Cells(i, 7) Value = Value + (Amount * AdjClose) Worksheets("Own Portfolio").Cells(i, 2) = Value Next Next 'Weights TotalValue = 0 For j = 1 To Stocks Symbol = Worksheets("Own Portfolio").Cells(1 + j, 4) Amount = Worksheets("Own Portfolio").Cells(1 + j, 7) AdjClose = Worksheets(Symbol).Cells(2, 7) TotalValue = TotalValue + (Amount * AdjClose) Next For j = 1 To Stocks StockValue = 0 Symbol = Worksheets("Own Portfolio").Cells(1 + j, 4) Amount = Worksheets("Own Portfolio").Cells(1 + j, 7) AdjClose = Worksheets(Symbol).Cells(2, 7) StockValue = Amount * AdjClose Worksheets("Own Portfolio").Cells(1 + j, 5) = StockValue / TotalValue Next 'Mean,Variance,Std Dev and Sharp Ratio '------------------------------------------------------------------------- '----------------This is where I get the error message-------------------- MeanVector = Worksheets("TempSheet").Range(Cells(2, 2), Cells(Stocks + 1, 2)) '----------------------------------------------------------------------- WeightsVector = Worksheets("Own Portfolio").Range(Cells(2, 5), Cells(Stocks + 1, 2)) Mean = Application.WorksheetFunction.SumProduct(MeanVector, WeightsVector) Call OwnPortfolioGraph(Symbol) Application.ScreenUpdating = True Application.DisplayAlerts = True Application.StatusBar = False End Sub Picture of "TempSheet"
change this: MeanVector = Worksheets("TempSheet").Range(Cells(2, 2), Cells(Stocks + 1, 2)) to this: set MeanVector = Worksheets("TempSheet").Range(Cells(2, 2), Cells(Stocks + 1, 2)) and see if it works then do the same for WeightsVector = Worksheets("Own Portfolio").Range(Cells(2, 5), Cells(Stocks + 1, 2)) Edit: I'm pretty sure that in your formula Worksheets("TempSheet").Range(Cells(2, 2), Cells(Stocks + 1, 2)) Unless the current worksheet is tempsheet then Cells(2, 2) will not be the value your expecting, neither will Cells(Stocks + 1, 2). you could try: With Worksheets("TempSheet") WeightsVector = .Range(.Cells(2, 5), .Cells(Stocks + 1, 2)) end with although looking at the screenshot you have added it doesnt appear cells(2,5) is from tempsheet
VBA- clearing the contents of previously filled cells
I have 2 column table with columns as bunker index and bunker name.. i have to index the bunker using VBA and then generate another table with columns as the Bunker names and 'first column as the date... On changing the number of bunkers( like from 10 to 8).. the color of the cells(last 2 columns previosly filled) in second table remains the same (blue), which i require as default (white) I have used many variables from my workbook. Any suggestion in this regard. As to how should the formatting be done? This is the Code : Sub Bunker_index_Click() Sheet3.Range(Sheet3.Cells(4, 6), Sheet3.Cells(500, 100)).ClearContents Dim N As Integer Dim i As Integer Dim j As Integer Dim k As Integer Dim LocalIYM As Date LocalIYM = ThisWorkbook.IYM N = Application.CountA(Range("B:B")) - 1 ThisWorkbook.NumBunker = N MsgBox (N) Cells(5, 1).Value = "Bunker Index" For i = 1 To 100 If i <= N Then Cells(5 + i, 1).Value = i Cells(5 + i, 1).Interior.Color = RGB(253, 233, 217) Cells(5 + i, 2).Interior.Color = RGB(219, 238, 243) Else Cells(5 + i, 1).ClearContents Cells(5 + i, 2).ClearContents End If Next i Range("F5").Value = "Time (LOCKED)" Range("F5").Interior.Color = RGB(253, 233, 217) For i = 1 To 100 If i <= N Then Cells(5, 6 + i).Value = Cells(5 + i, 2) Cells(5, 6 + i).Interior.Color = RGB(253, 233, 217) Else Cells(5, 6 + i).ClearContents ' unable to bring back th original ' color of the cells End If Next i For k = 1 To 12 * 1 If k <= ThisWorkbook.N Then Cells(k + 5, 6).Value = LocalIYM LocalIYM = DateAdd("m", 1, LocalIYM) Cells(5 + k, 6).Interior.Color = RGB(253, 233, 217) ' problem handling borders ' Range("B2").Borders(xlEdgeLeft).LineStyle = XlLineStyle.xlDashDot ' 1edgeleft).LineStyle = x1linestyle.x1countinous For j = 1 To N Cells(5 + k, 6 + j).Interior.Color = RGB(219, 238, 243) Next j Else Sheet2.Cells(i + 4, 6).Clear End If Next k End Sub
Instead of .clear or .clearcontents Try: .Interior.Pattern = xlNone