提高 For 循环效率 VBA
Improving For Loop Efficiency VBA
目前我在 excel 电子表格中导入了 10Kx15 行的原始数据。
我有许多已清理的字段,但感兴趣的是一个名为 "Hazard" 的字段。对于遇到的每个危险实例,我们都需要将其删除。
这是我用来清理(部分)我的数据集的代码:
Sub dataCleanse()
Dim Last
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Last = Cells(Rows.Count, "F").End(xlUp).Row
For i = Last To 1 Step -1
If (Cells(i, "F").Value) = "Hazard" Then
Cells(i, "A").EntireRow.Delete
End If
Next i
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
处理 10,000 条左右的记录需要 10-15 秒。我尝试过使用自动过滤器,但是当我使用 .EntireRow.Delete 时,它会删除过滤条件下的行。
即如果我们有第 1 行和第 3 行 'Hazard' 并使用自动过滤器,它也会删除没有 'Hazard'.
的第 2 行
我还先将计算设置为手动,然后再设置为自动,这样就不会每次都刷新。
有什么建议可以提高我的宏速度吗?
谢谢!
我不确定这是否会更快,但我的建议是 select F 列,找到 "Hazard" 的实例,删除该行,然后重复该过程直到 [=15] =] 在 F 列中找不到。
Dim iRow As Integer
Application.ScreenUpdating = False
Columns("F:F").Select
Set RangeObj = Selection.Find(What:="Hazard", LookIn:=xlValues, MatchCase:=True)
Do Until (RangeObj Is Nothing)
iRow = RangeObj.Row
Rows(iRow & ":" & iRow).Delete
Columns("F:F").Select
Set RangeObj = Selection.Find(What:="Hazard", LookIn:=xlValues, MatchCase:=True)
Loop
Application.ScreenUpdating = True
请试一试。
此解决方案对于小型数据集来说并不快,但对于非常大的数据集来说会更快。代码看起来更长,但处理数组比操作工作簿快得多。 (我相信有更有效的方法来缩短数组)。顺便说一句 - 你的代码在我放在一起的示例数据集上对我有用。如果这对您的数据不起作用,请post一个您输入的小例子以及结果应该是什么样子。
示例输入:
宏的输出:
使用数组的宏代码:
Option Explicit
Sub dataCleanse2()
Dim nRows As Long, nCols As Long
Dim i As Long, j As Long, k As Long
Dim myRng As Range
Dim myArr() As Variant, myTmpArr() As Variant
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Set myRng = Sheets("Sheet1").UsedRange
myArr = myRng.Value2
nRows = UBound(myArr, 1)
nCols = UBound(myArr, 2)
For i = nRows To 1 Step -1
If CStr(myArr(i, 6)) = "Hazard" Then
ReDim Preserve myTmpArr(1 To nRows - 1, 1 To nCols)
For j = 1 To i - 1
For k = 1 To nCols
myTmpArr(j, k) = myArr(j, k)
Next k
Next j
For j = i To nRows - 1
For k = 1 To nCols
myTmpArr(j, k) = myArr(j + 1, k)
Next k
Next j
nRows = UBound(myTmpArr, 1)
Erase myArr
myArr = myTmpArr
Erase myTmpArr
End If
Next i
myRng.Clear
Set myRng = Sheets("Sheet1").Range(Cells(1, 1), Cells(nRows, nCols))
myRng.Value2 = myArr
Set myRng = Nothing
Erase myArr
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
您可以采用以下 Autofilter
方法
Option Explicit
Sub dataCleanse()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.DisplayAlerts = False
With ActiveSheet
' insert "dummy" header cell for Autofilter to work
.Range("F1").Insert
.Range("F1").value = "header"
With .Range("F1", .Cells(.Rows.Count, "F").End(xlUp))
.AutoFilter Field:=1, Criteria1:="Hazard"
With .Offset(1).Resize(.Rows.Count - 1)
If Application.WorksheetFunction.Subtotal(103, .Columns(1)) > 1 Then .SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
.AutoFilter
End With
.Range("F1").Delete 'remove "dummy" header cell
End With
Application.DisplayAlerts = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
处理 250 列的 10,000 条记录,每条记录不到一秒
感谢大家的帮助!我选择了另一种方法(除了一个用户发布的方法),它使用了一个数组。我仍然在熟悉数组(对 VBA / 一般编程来说是新手),但发现当我将值加载到数组中时,速度提高了大约 50%!我不知道为什么加载到数组中要快得多的确切原因,但我假设这是因为它将数组作为聚合而不是单个单元格值进行处理。
Sub CleanseAction()
Dim Last
Dim prevCalcMode As Variant
Application.ScreenUpdating = False
prevCalcMode = Application.Calculation
Application.Calculation = xlCalculationManual
Last = Cells(Rows.Count, "H").End(xlUp).Row
For i = Last To 1 Step -1
If (Cells(i, "H").Value) = "Hazard" Then
Cells(i, "A").EntireRow.Delete
End If
Next i
Application.Calculation = prevCalcMode
Application.ScreenUpdating = True
目前我在 excel 电子表格中导入了 10Kx15 行的原始数据。 我有许多已清理的字段,但感兴趣的是一个名为 "Hazard" 的字段。对于遇到的每个危险实例,我们都需要将其删除。
这是我用来清理(部分)我的数据集的代码:
Sub dataCleanse()
Dim Last
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Last = Cells(Rows.Count, "F").End(xlUp).Row
For i = Last To 1 Step -1
If (Cells(i, "F").Value) = "Hazard" Then
Cells(i, "A").EntireRow.Delete
End If
Next i
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
处理 10,000 条左右的记录需要 10-15 秒。我尝试过使用自动过滤器,但是当我使用 .EntireRow.Delete 时,它会删除过滤条件下的行。 即如果我们有第 1 行和第 3 行 'Hazard' 并使用自动过滤器,它也会删除没有 'Hazard'.
的第 2 行我还先将计算设置为手动,然后再设置为自动,这样就不会每次都刷新。
有什么建议可以提高我的宏速度吗?
谢谢!
我不确定这是否会更快,但我的建议是 select F 列,找到 "Hazard" 的实例,删除该行,然后重复该过程直到 [=15] =] 在 F 列中找不到。
Dim iRow As Integer
Application.ScreenUpdating = False
Columns("F:F").Select
Set RangeObj = Selection.Find(What:="Hazard", LookIn:=xlValues, MatchCase:=True)
Do Until (RangeObj Is Nothing)
iRow = RangeObj.Row
Rows(iRow & ":" & iRow).Delete
Columns("F:F").Select
Set RangeObj = Selection.Find(What:="Hazard", LookIn:=xlValues, MatchCase:=True)
Loop
Application.ScreenUpdating = True
请试一试。
此解决方案对于小型数据集来说并不快,但对于非常大的数据集来说会更快。代码看起来更长,但处理数组比操作工作簿快得多。 (我相信有更有效的方法来缩短数组)。顺便说一句 - 你的代码在我放在一起的示例数据集上对我有用。如果这对您的数据不起作用,请post一个您输入的小例子以及结果应该是什么样子。
示例输入:
宏的输出:
使用数组的宏代码:
Option Explicit
Sub dataCleanse2()
Dim nRows As Long, nCols As Long
Dim i As Long, j As Long, k As Long
Dim myRng As Range
Dim myArr() As Variant, myTmpArr() As Variant
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Set myRng = Sheets("Sheet1").UsedRange
myArr = myRng.Value2
nRows = UBound(myArr, 1)
nCols = UBound(myArr, 2)
For i = nRows To 1 Step -1
If CStr(myArr(i, 6)) = "Hazard" Then
ReDim Preserve myTmpArr(1 To nRows - 1, 1 To nCols)
For j = 1 To i - 1
For k = 1 To nCols
myTmpArr(j, k) = myArr(j, k)
Next k
Next j
For j = i To nRows - 1
For k = 1 To nCols
myTmpArr(j, k) = myArr(j + 1, k)
Next k
Next j
nRows = UBound(myTmpArr, 1)
Erase myArr
myArr = myTmpArr
Erase myTmpArr
End If
Next i
myRng.Clear
Set myRng = Sheets("Sheet1").Range(Cells(1, 1), Cells(nRows, nCols))
myRng.Value2 = myArr
Set myRng = Nothing
Erase myArr
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
您可以采用以下 Autofilter
方法
Option Explicit
Sub dataCleanse()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.DisplayAlerts = False
With ActiveSheet
' insert "dummy" header cell for Autofilter to work
.Range("F1").Insert
.Range("F1").value = "header"
With .Range("F1", .Cells(.Rows.Count, "F").End(xlUp))
.AutoFilter Field:=1, Criteria1:="Hazard"
With .Offset(1).Resize(.Rows.Count - 1)
If Application.WorksheetFunction.Subtotal(103, .Columns(1)) > 1 Then .SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
.AutoFilter
End With
.Range("F1").Delete 'remove "dummy" header cell
End With
Application.DisplayAlerts = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
处理 250 列的 10,000 条记录,每条记录不到一秒
感谢大家的帮助!我选择了另一种方法(除了一个用户发布的方法),它使用了一个数组。我仍然在熟悉数组(对 VBA / 一般编程来说是新手),但发现当我将值加载到数组中时,速度提高了大约 50%!我不知道为什么加载到数组中要快得多的确切原因,但我假设这是因为它将数组作为聚合而不是单个单元格值进行处理。
Sub CleanseAction()
Dim Last
Dim prevCalcMode As Variant
Application.ScreenUpdating = False
prevCalcMode = Application.Calculation
Application.Calculation = xlCalculationManual
Last = Cells(Rows.Count, "H").End(xlUp).Row
For i = Last To 1 Step -1
If (Cells(i, "H").Value) = "Hazard" Then
Cells(i, "A").EntireRow.Delete
End If
Next i
Application.Calculation = prevCalcMode
Application.ScreenUpdating = True