【问题标题】:VBA-Excel and large data sets causes program to crashVBA-Excel 和大型数据集导致程序崩溃
【发布时间】:2011-04-22 02:22:03
【问题描述】:

第一次发布海报,一般来说是编程新手。我有一个项目,我必须建立一个财务模型来挖掘 excel 中的数据。我已经成功地在 VBA 上构建了上述模型。我已经对 3,000 行数据集进行了测试,并且成功。我将简要解释它的作用。

我在给定的一天在多个交易所跟踪给定的股票。我下载数据(大约 935,000 行) 第一步是将给定交易所的所有数据(大约 290,000)复制到新表上(这大约需要 8 分钟),然后我创建一个新列来记录买卖差价(12 秒) ),下一步是我遇到的问题,我基本上对每行数据进行两次排名,一列用于出价大小,一列用于要价大小。我创建了一个函数,它使用 excel Percentile 函数并根据给定的出价和要价大小的位置进行排名。截至目前,我在过去 35 分钟内一直在运行宏,但尚未执行。我不能尝试其他宏,因为每个宏都依赖于前一个。

所以我的基本问题是,由于我的数据集很大,我的模型不断崩溃。代码在处理测试数据时似乎很好,并且在我运行程序时它不会抛出任何错误,但是更大的数据集它只是崩溃。有没有人有什么建议?数据量这么大,这正常吗?

提前致谢。 假的

这是给我带来麻烦的 sub 和 function,sub 接受所需的输入来运行该函数,然后弹出到分配的单元格中。该代码假设为三个单独的工作表重复该过程。目前,我喜欢它在一张纸上工作,因此使用 cmets 不包括循环

Sub Bucketting()

Dim firstRow As Long
Dim lastRow As Long
Dim counter As Long
Dim bidRange As Range
Dim offerRange As Range
Dim bidScroll As Range
Dim offerScroll As Range
Dim Ex As String
Dim i As Integer

'For i = 1 To 1 Step 1 'Sheet Selection Process
 '   If i = 1 Then
  '      Ex = "Z"
   ' ElseIf i = 2 Then
    '    Ex = "P"
   ' Else
    '    Ex = "T"
   ' End If

Sheets("Z").Select 'Sheet selected

With ActiveSheet

    firstRow = .UsedRange.Cells(1).Row + 1
    lastRow = .UsedRange.Rows.Count

   Set bidRange = .Range("F2:F" & lastRow)
   Set offerRange = .Range("G2:G" & lastRow)

    For counter = lastRow To firstRow Step -1

        Set bidScroll = .Range("F" & counter)
        Set offerScroll = .Range("G" & counter)

        With .Cells(counter, "J")
        .Value = DECILE_RANK(bidRange, bidScroll)
        End With

        With .Cells(counter, "K")
        .Value = DECILE_RANK(offerRange, offerScroll)
        End With

    Next counter

End With

Range("J1").Select
ActiveCell = "Bid Rank"

ActiveCell.Offset(0, 1) = "Offer Rank"

'Next i

End Sub

 Function DECILE_RANK(DataRange, RefCell)

    'Credit: BJRaid 
    'DECILE_RANK(The Range of data)
    'Declares the function that can be called in the spreadsheet cell  - enter '=DECILE_RANK(A5:A50,A5)

    'Using the percentile worksheet function calculate where the 10th, 20th etc percentile of the reference range are

    DEC1 = Application.WorksheetFunction.Percentile(DataRange, 0.1)
    DEC2 = Application.WorksheetFunction.Percentile(DataRange, 0.2)
    DEC3 = Application.WorksheetFunction.Percentile(DataRange, 0.3)
    DEC4 = Application.WorksheetFunction.Percentile(DataRange, 0.4)
    DEC5 = Application.WorksheetFunction.Percentile(DataRange, 0.5)
    DEC6 = Application.WorksheetFunction.Percentile(DataRange, 0.6)
    DEC7 = Application.WorksheetFunction.Percentile(DataRange, 0.7)
    DEC8 = Application.WorksheetFunction.Percentile(DataRange, 0.8)
    DEC9 = Application.WorksheetFunction.Percentile(DataRange, 0.9)


    ' Calculate the Decile rank that the reference cell value sits within

    If (RefCell <= DEC1) Then DECILE_RANK = 1
    If (RefCell > DEC1) And (RefCell <= DEC2) Then DECILE_RANK = 2
    If (RefCell > DEC2) And (RefCell <= DEC3) Then DECILE_RANK = 3
    If (RefCell > DEC3) And (RefCell <= DEC4) Then DECILE_RANK = 4
    If (RefCell > DEC4) And (RefCell <= DEC5) Then DECILE_RANK = 5
    If (RefCell > DEC5) And (RefCell <= DEC6) Then DECILE_RANK = 6
    If (RefCell > DEC6) And (RefCell <= DEC7) Then DECILE_RANK = 7
    If (RefCell > DEC7) And (RefCell <= DEC8) Then DECILE_RANK = 8
    If (RefCell > DEC8) And (RefCell <= DEC9) Then DECILE_RANK = 9
    If (RefCell > DEC9) Then DECILE_RANK = 10

End Function

【问题讨论】:

  • 您使用什么代码访问单元格?
  • 您是否已通过代码尝试查看具体可能无法按预期工作?也许是无限循环或其他什么?
  • Nick-当我单步执行代码时,它工作得很好。
  • Lance-Im 使用 vba,我应该发布实际代码吗?
  • 我认为您的问题过于宽泛/模棱两可,无法获得具体答案。您肯定在 Excel 中使用了大量数据,是的,但是您能否缩小所有问题的范围?你是什​​么意思'崩溃'? excel会死还是出现VBA错误?你是说如果你单步执行代码就可以完美运行,但当你让它运行时就不行?等等等等……

标签: excel optimization vba


【解决方案1】:

935,000 行对于 Excel 来说已经很多了。喜欢,真的很多。除非使用真正的数据库,否则如果您的应用程序实际上是在每个单元格中放置一个 =Percentile(...),我建议您尝试使用其他工具。也许是 VBA 本身的东西。更一般地说,使用单元格之外的东西 - 然后将结果值存储在单元格中。维护那些与 935k 行数据相互依赖的公式需要大量开销。

【讨论】:

  • Jody - 百分位数不在 935k 行上运行,而是在 290K 行上运行。那还是很多线吗?
  • 我会这么说。 Excel 2003 将您限制为 65,000 行。这是一个相当不错的数字,可以切换到更重的任务。与迁移到成熟的编程语言/数据库相比,使用访问权限可以让您更轻松地在 excel 中迁移您现在正在做的事情。
  • Jody-Im 使用 excel 2010。我不熟悉 access,我必须安装它,但 access 会运行 excel 运行的所有代码吗?
  • 我提到了 2003 年作为参考点。您可以将 VBA 与 Access 一起使用,但需要修改代码。 Access 没有“单元格”,而“工作表”有表、列和记录集(哦,我的)。这将使您再次经历漫长的学习之旅,但您肯定会在那里处理大量数据。
  • @Sham:您也许可以编写一个效率更高的百分位数函数。您不需要 290K 数据点。你可以用 1000 个随机选择的点甚至 100 个点来构建它,除非你真的担心分布尾部的点。
【解决方案2】:

问题是您单独循环遍历每一行,Excel 方法是尽可能尝试一次处理整个范围。我会将范围加载到数组中,然后修改您的 DECILE_RANK 代码以使用数组中的项目。

请注意,读取范围的变量数组是二维的。

这是功能齐全的代码,包括我的自定义 VBA 数组切片器。请注意,它仅在一个小数据集上进行了测试:

Sub Bucketting()

Dim lastRow As Long
Dim bidArray As Variant
Dim offerArray As Variant

Sheets("Sheet1").Select 'Sheet selected

With ActiveSheet

  lastRow = .UsedRange.Rows.Count + 1

  bidArray = .Range("F2:F" & lastRow)
  offerArray = .Range("G2:G" & lastRow)

  Range("J2:J" & lastRow).Value = GetArraySlice2D(DECILE_RANK(bidArray), "column", 1, 1, 0)
  Range("K2:K" & lastRow).Value = GetArraySlice2D(DECILE_RANK(offerArray), "column", 1, 1, 0)

End With

Range("J1").Select
ActiveCell = "Bid Rank"

ActiveCell.Offset(0, 1) = "Offer Rank"

End Sub

Function DECILE_RANK(DataRange As Variant) As Variant

' Credit:     BJRaid
' DECILE_RANK(The Range of data)
' Declares the function that can be called in the spreadsheet cell  - enter '=DECILE_RANK(A5:A50,A5)

Dim DEC(0 To 10) As Variant
Dim i As Integer, j As Integer

'Using the percentile worksheet function calculate where the 10th, 20th etc percentile of the reference range are
DEC(0) = 0
For i = 1 To 9
  DEC(i) = Application.WorksheetFunction.Percentile(DataRange, 0.1 * i)
Next i
DEC(10) = Application.WorksheetFunction.Max(DataRange)

' Calculate the Decile rank that the reference cell value sits within
For i = 1 To UBound(DataRange, 1)
  For j = 1 To 10
    If ((DataRange(i, 1) > DEC(j - 1)) And (DataRange(i, 1) <= DEC(j))) Then
      DataRange(i, 1) = j
      Exit For
    End If
  Next j
Next i

DECILE_RANK = DataRange

End Function

Public Function GetArraySlice2D(Sarray As Variant, Stype As String, Sindex As Integer, Sstart As Integer, Sfinish As Integer) As Variant

' this function returns a slice of an array, Stype is either row or column
' Sstart is beginning of slice, Sfinish is end of slice (Sfinish = 0 means entire
' row or column is taken), Sindex is the row or column to be sliced (NOTE:
' 1 is always the first row or first column)
' an Sindex value of 0 means that the array is one dimensional 3/20/09 Lance Roberts

Dim vtemp() As Variant
Dim i As Integer

On Err GoTo ErrHandler

Select Case Sindex
    Case 0
        If Sfinish - Sstart = UBound(Sarray) - LBound(Sarray) Then
            vtemp = Sarray
        Else
            ReDim vtemp(1 To Sfinish - Sstart + 1)
            For i = 1 To Sfinish - Sstart + 1
                vtemp(i) = Sarray(i + Sstart - 1)
            Next i
        End If
    Case Else
        Select Case Stype
            Case "row"
                If Sfinish = 0 Or (Sstart = LBound(Sarray, 2) And Sfinish = UBound(Sarray, 2)) Then
                    vtemp = Application.WorksheetFunction.Index(Sarray, Sindex, 0)
                Else
                    ReDim vtemp(1 To Sfinish - Sstart + 1)
                    For i = 1 To Sfinish - Sstart + 1
                        vtemp(i) = Sarray(Sindex, i + Sstart - 1)
                    Next i
                End If
            Case "column"
                If Sfinish = 0 Or (Sstart = LBound(Sarray, 1) And Sfinish = UBound(Sarray, 1)) Then
                    vtemp = Application.WorksheetFunction.Index(Sarray, 0, Sindex)
                Else
                    ReDim vtemp(1 To Sfinish - Sstart + 1)
                    For i = 1 To Sfinish - Sstart + 1
                        vtemp(i) = Sarray(i + Sstart - 1, Sindex)
                    Next i
                End If
        End Select
End Select
GetArraySlice2D = vtemp
Exit Function

ErrHandler:
    Dim M As Integer
    M = MsgBox("Bad Array Input", vbOKOnly, "GetArraySlice2D")

End Function

【讨论】:

  • @Sham,我编辑了几次,确保你得到了数组切片例程的正确参数。我明天去看看。
  • @Lance。我已经有一段时间了,我的经验不足真的很明显。我似乎无法让 Decile_Rank 函数捕捉参考单元格,它一直弹出空。当我逐步执行程序并进入您的数组切片例程时,它表明它无法获取工作表函数类的 Index 属性。这是我整个模型中唯一不工作的一方,我设法检查了其他所有内容,即使使用这个庞大的数据集,它们也能正常工作。我真的很感激这一切,相信我!!!
  • @Sham,我签到并看到了您的评论。我需要更多关于 Decile_Rank 函数没有“捕获”参考单元格的说明。我不确定为什么 Index 属性有问题(尽管我相信你可以直接从 Application 对象中得到它)。
  • @Lance。感谢你的宝贵时间。基本上问题是每一行都有一个报价大小,我需要的是从 1-10 对这些报价大小进行存储(排名),我发现最好的方法是使用百分位函数,然后运行一个 if 语句,如您所见。但是当我单步执行代码时,当需要对给定单元格进行排名时,RefCell 状态为空(当您将鼠标放在它上面时)。至于索引属性,它对我不起作用,我不明白。
  • @Sham,您是否更改了 Decile_Rank 函数以使用数组?
【解决方案3】:

我不确定这是否会直接解决您的问题,但您是否考虑过使用Application.ScreenUpdating = False?处理完数据后,不要忘记将其设置回 true。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-03
    • 1970-01-01
    • 1970-01-01
    • 2016-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多