我有两列,日期和每日回报。我每天有7000左右的回报,我希望按每年每个月的复合回报对它们进行分组。
我创建了一个VBA函数来解决这个问题:
Function MonthlyRet(YearNum, MonthNum, DateData, RetData) As Double
Dim nValues As Integer
nValues = DateData.Rows.Count
MonthlyRet = 1
For i = 2 To nValues
If (IsNumeric(RetData(i)) And Year(DateData(i)) = YearNum And Month(DateData(i)) = MonthNum) Then
MonthlyRet = MonthlyRet * RetData(i)
End If
Next i
End Function
我可以使用以下方法在我的数据上运行:
=MonthlyRet(2015,1,Data!$A$1:$A$7000,Data!$AR$1:$AR$7000)-1
这将在我的数据集中返回2015年1月的复合回报。然而,这似乎有效,当我将方程复制并粘贴到1990年至2015年的所有月份,大约300个单元时,在快速计算机上完成操作需要相当长的时间。
该算法通过7000条if语句循环300次。这对我来说似乎不是那么多迭代,但我的计算机将需要一两分钟来计算它们。在java中,我知道结果将是毫秒,但我是VBA新手。
有没有办法在VBA中提高此例程的性能?
答案 0 :(得分:0)
有很多方法可以提高性能。你有什么限制?
如果您只想提高UDF的VBA性能,我认为您无法做到。这些变化可能会有所帮助:
Function MonthlyRet(YearNum, MonthNum, DateData, RetData) As Double
Dim nValues As Integer, i as Integer:nValues = DateData.Rows.Count:MonthlyRet = 1
For i = 2 To nValues
If (IsNumeric(RetData(i)) And Year(DateData(i)) = YearNum And Month(DateData(i)) = MonthNum) Then MonthlyRet = MonthlyRet * RetData(i)
Next i
End Function
我写了一篇关于提高VBA表现的帖子here。
Excel XLL将为您的UDF功能启用多线程。一个很好的视频: 点击此处:https://www.youtube.com/watch?v=L2ALSgK1LLY
我个人会将此选项作为(a)
使用它可以让您更好地控制函数何时刷新并防止“冻结Excel”和(b)
这将比单独的UDF函数运行稍快一些。
答案 1 :(得分:0)
正如我上面所写,当使用VBA查看大范围时,最好将该范围读入数组,然后通过数组。修改你的UDF;并且明确声明所有变量和数据类型 - 在VBA中总是一件好事,我们最终得到:
Option Explicit
Function MonthlyRet(YearNum As Long, MonthNum As Long, DateData As Range, RetData As Range) As Double
Dim vDates As Variant, vReturns As Variant
Dim I As Long
Dim D As Double
vDates = DateData
vReturns = RetData
D = 1
For I = 2 To UBound(vDates, 1)
If (IsDate(vDates(I, 1)) And Year(vDates(I, 1)) = YearNum And Month(vDates(I, 1)) = MonthNum) Then
D = D * vReturns(I, 1)
End If
Next I
MonthlyRet = D
End Function
但是,我建议您查看数据透视表。在我看来,这对于显示您的数据是理想的。例如,您可以将日期拖动到行区域;将数据返回到列区域;将值字段设置更改为产品;然后按月和年对行进行分组 - 最后会找到一个可以显示相同结果的漂亮表格。
顺便说一句,我不确定你的算法会返回你期望的值。一旦你对它进行了排序,不确定是否更容易使用Pivot或UDF。