【问题标题】:Excel VBA Write Array without a For LoopingExcel VBA 写入数组而不使用 For 循环
【发布时间】:2021-10-15 22:42:24
【问题描述】:

我有一个包含 30,000 行和 28 列数据的电子表格。我正在寻找以特定方式对数据进行编码。数据由字符串和数字的混合组成。对于每一行(第 1 到 28 列),我需要将每个单元格中的每个字符转换为一个数字。我有一本字典来进行转换。其中字符是键,值是编码。

我的代码可以运行,但是速度有点慢。完成任务需要 30 多分钟。鉴于我们正在查看的数据量,这是可以理解的。 30, 000 行 x 28 列 x N 个字符。很多。

以下代码的快速说明:

  1. 循环遍历 Range 中的每个单元格(30,000 行,28 列)
  2. 对于每一行,将所有值连接成一个字符串
  3. 将大字符串逐个字符传递到字典中,检索编码值(数字)。
  4. 将编码字符串写入工作表。每个值都有自己的单元格。

我猜瓶颈是当我在循环中将编码写入工作表时。我想知道他们是否是一种更快的方法?

Sub main()

Application.DisplayAlerts = False
Application.ScreenUpdating = False

'initialize dictionary for encoding data on another module
Globals.initialize_globals

'loop through each record
Dim wkbook As Workbook: Set wkbook = Workbooks.Application.ActiveWorkbook
Dim wksheet As Worksheet: Set wksheet = wkbook.Worksheets("Raw Data")
Dim lastRow As Integer: lastRow = wksheet.Cells(Rows.Count, 1).End(xlUp).Row
Dim stringbuilder As String
Dim encoding() As Integer
Dim char_count As Integer
Dim i, ii, e As Integer

'loop through rows, columns and encode string data
For i = 2 To lastRow
    'loop through columns
    For ii = 1 To 27
        'concatenate each cell value as a large string
        stringbuilder = stringbuilder & wksheet.Range(Cells(i, ii), Cells(i, ii)).Value
    Next
    encoding = EncodeString(stringbuilder)
    stringbuilder = ""
    For e = 1 To UBound(encoding)
        'write encoding onto sheet
        wksheet.Range(Cells(i, 33 + e), Cells(i, 33 + e)) = encoding(e)
    Next
Next

Application.DisplayAlerts = True
Application.ScreenUpdating = True

End Sub

Function EncodeString(str As String) As Integer()
Dim encoded() As Integer
ReDim encoded(1 To Len(str))

For i = 1 To Len(str)
    ' build an encoded string by passing in each character as a key, and retrieve encoded value
    encoded(i) = Parameters.Item(Mid(str, i, 1))
Next
EncodeString = encoded
End Function

【问题讨论】:

  • 什么是“参数”?问题中提到的字典被声明为全局变量?有一些方法可以提高代码速度:单元之间的迭代比数组元素之间的迭代快得多。因此,将要处理的范围放在一个数组中。然后,在单元格中写入非常耗时。尝试声明另一个数组并使用编码值加载它,然后将数组内容一次放在代码末尾。
  • 你能估计最大N(一行的字符数)吗?至少可以确保支付必要的金额......

标签: excel vba performance


【解决方案1】:

请尝试下一个更新的代码:

Sub main()
'initialize dictionary for encoding data on another module
Globals.initialize_globals

Dim wkbook As Workbook: Set wkbook = Workbooks.Application.ActiveWorkbook
Dim wksheet As Worksheet: Set wksheet = wkbook.Worksheets("Raw Data")
Dim lastRow As Integer: lastRow = wksheet.cells(rows.count, 1).End(xlUp).row
Dim stringbuilder As String, encoding() As Integer, char_count As Integer
Dim i As Long, ii As Long, e As Long
Dim arr, arrFin, N As Long, maxCol As Long 'new variables

N = 1000 'the estimated maximum number of characters on a row
arr = wksheet.Range("A2:AA" & lastRow).Value
ReDim arrFin(1 To UBound(arr), 1 To N)
'loop through array rows, columns and encode string data
For i = 2 To UBound(arr)
    For ii = 1 To UBound(arr, 2)
        'concatenate each cell value as a large string
        stringbuilder = stringbuilder & arr(i, ii) 
    Next ii
    encoding = EncodeString(stringbuilder)
    stringbuilder = ""
    If UBound(encoding) > UBound(arrFin, 2) Then
        maxCol = UBound(encoding)
        ReDim Preserve arrFin(1 To UBound(arr), 1 To maxCol)
    End If
    For e = 1 To UBound(encoding)
        arrFin(i, e) = encoding(e)
    Next e
Next i
if maxCol = 0 Then maxCol =ubound(arrFin, 2)
'drop the array content at once:
wksheet.Range("AG2").Resize(UBound(arrFin), maxCol).Value = arrFin
End Sub

请使用您现有的函数进行编码...

当然,没有经过测试,但这应该是使代码更快的想法。事实上,我尝试将我在评论中提出的建议放入代码中。

【讨论】:

  • @junfanbl 没抽时间测试一下上面的代码吗?如果经过测试,它没有解决您的问题吗?如果出现错误,请说明是什么错误以及在哪一行。这应该会使您的代码更快,但没有经过测试,它可能需要一些调整......
【解决方案2】:

VBA for Excel 的瓶颈是从 VBA 访问 Excel,因此您可以通过将范围内的数据读取到二维数组中并处理数组中的数据来显着减少运行时间。

同样,您可以将输出数据收集到一个数组中并立即将其写回。在您的特殊情况下,逐行写入数据可能更容易(对于每一行,您可能有不同长度的数据),但至少您可以一次转储一行的数据。

VBA 中的字符串处理速度相当快,您不必担心。

这应该相当快:

data = wksheet.Range(wksheet.Cells(2, 1), wksheet.Cells(lastRow, 27))
Dim row As Long, col As Long

For row = LBound(data, 1) To UBound(data, 1)
    Dim stringbuilder As String
    stringbuilder = ""
    For col = LBound(data, 2) To UBound(data, 2)
        'concatenate each cell value as a large string
        stringbuilder = stringbuilder & data(row, col)
    Next

    If stringbuilder <> "" Then
        Dim encoding() As Long
        encoding = EncodeString(stringbuilder)
    End If
    wksheet.Cells(row + 1, 33).Resize(1, UBound(encoding)) = encoding
Next

请注意,您始终应在 VBA 中使用 Long 而不是 Integer。它避免了溢出,而且速度更快(而且它不使用更多的内存)

更新 做了一个基准测试(普通电脑,Excel 2016)。 30k 行 * 26 列,混合(随机)数据(使用虚拟编码函数将每个字符转换为其 ascii 值)。大约花了。 7s 执行。
我还使用大型二维输出数组进行了变体(就像 FaneDuru 在他的回答中所做的尝试),但这需要更长的时间(13 秒)。

【讨论】:

    猜你喜欢
    • 2017-11-21
    • 2017-12-01
    • 2020-02-21
    • 2013-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多