Excel VBA函数向范围内的单元格添加值并获取产品

Excel VBA函数向范围内的单元格添加值并获取产品,excel,function,vba,Excel,Function,Vba,我一直在为这件事焦头烂额,在无休止的网上搜索后仍然找不到答案 下面是我要找的内容的一个快速总结,下面是一个较长的解释 我需要一个VBA函数来复制类似这样的公式:{=product(a:a+1)}。这将遍历A列中的每个单元格,其中有一个数字,然后加1,然后相乘。这可以使用(1)Evaluate(除非范围中有空格或文本)或(2)worksheetfunction.product(工作表函数.product)来完成,但当我查看范围并添加1时,它不喜欢它 如果有人能想出使用这两种方法中的任何一种的解决方

我一直在为这件事焦头烂额,在无休止的网上搜索后仍然找不到答案

下面是我要找的内容的一个快速总结,下面是一个较长的解释

我需要一个VBA函数来复制类似这样的公式:{=product(a:a+1)}。这将遍历A列中的每个单元格,其中有一个数字,然后加1,然后相乘。这可以使用(1)Evaluate(除非范围中有空格或文本)或(2)worksheetfunction.product(工作表函数.product)来完成,但当我查看范围并添加1时,它不喜欢它

如果有人能想出使用这两种方法中的任何一种的解决方案,我都会欣喜若狂。我也可能错过了一些非常基本的东西

更长的版本

我想做的是: 创建三个VBA函数,用于计算1)复合回报2)年化复合回报和3)年化波动率。对于可变范围和年度化函数,您还可以指定是天、月还是年。例如,使用复合返回函数时会显示“=compoundreturn(范围、句号)”

计算很容易使用公式,但我想创建函数,使其成为一个快速的过程

我可以使用EVALUATE通过VBA使所有三个都工作

它们看起来像这样

Function PerRet(rng As range)
    PerRet = Worksheet.Evaluate("=exp(sumproduct(ln(" & rng.Address & "+1)))-1")
End Function

Function AnnRet(rng As range, np As Double)
    AnnRet = Evaluate("=exp((Average(ln(" & rng.Address & "+1)))*" & np & ")-1")
End Function

Function AnnVol(rng As range, np As Double)
    AnnVol = Worksheet.Evaluate("=stdev.s(ln(" & rng.Address & "+1))*sqrt(" & np & ")")
End Function
问题是,选择的范围有时可能会被整个列(其中包含空格和可能包含的文本)选中。当您只是在公式中使用AVERAGE或STDEV.S之类的函数时,它们将忽略这些。不幸的是,在使用Evaluate时,它们不会这样做

我知道我可以使用
worksheetfunction.AVERAGE
,而不是使用Evaluate来表示平均值,这样就可以忽略空格和文本。但在这里,我遇到了另一个问题,函数的这一部分必须查看一系列回报(正回报和负回报),并在平均之前为每个回报加1。这似乎不适用于某个范围,除非该范围只是一个单元格

到目前为止,我对此的一个例子是

Function AnnRet(rng As range, np As Double)  
    AnnRet = exp(WorksheetFunction.Average(WorksheetFunction.Ln(rng + 1)) * np) - 1      
End Function
在得到Ln然后求平均值之前,我能让它在范围内的每个单元格中加1吗

非常感谢您的关注 邓肯(Duncan)

一点光线测试表明,沿着这些思路可能可行:

Sub Tester()
Dim v

    v = ActiveSheet.Evaluate("=SUM(IF(ISBLANK(A:A),false,A:A+1))")

    Debug.Print v

End Sub
我只是在可乐里有几个数字,它只在单元格填充的地方加上(数字+1),也就是说,它并没有在单元格为空的地方加上一堆1

编辑:

在您对非数字单元格的问题发表意见后,我们会进行一些测试,例如:

Function Tester(rng As Range)
Dim v, f

    f = "=AVERAGE(IF(ISBLANK({a}),FALSE,IF(ISNUMBER({a}),LN({a}+1),FALSE)))"

    'remove any unsed part of the input range (for performance)
    Set rng = Application.Intersect(rng, rng.Parent.UsedRange)

    v = rng.Parent.Evaluate(Replace(f, "{a}", rng.Address(False, False)))


    Tester = v

End Function
首先过滤掉空格-然后可以将非空格安全地传递给ISNUMBER()以过滤掉非数字单元格。这不是你的最终公式,但离你不远。您是否有一些用于测试的输入和预期输出

编辑2:


使用
Intersect()
将整列输入范围限制在工作表的“已使用”部分似乎可以显著提高性能(当我在该范围内只有1000个值时,性能提高了约100倍)。

谢谢Tim。这适用于忽略空白单元格,但不适用于单元格中有文本的位置。我用“isnumber”尝试了相同的公式,它在excel中用作公式时有效,但奇怪的是,在VBA中使用Evaluate时不起作用。你知道为什么吗?这是我试图复制的公式“=EXP(AVERAGE)(IF(ISNUMBER(S:S),LN(S:S+1),FALSE))*12)-1”,这就是我得出的“Evaluate”(=EXP(AVERAGE)(IF(ISNUMBER(&rng.Address&)),LN(&rng.Address&“+1),FALSE))*“&np&”-1”),其中“rng”是我的范围,“np”是12。理想情况下,我想在不评估速度的情况下完成这一切。太棒了,这对我来说很好,蒂姆,非常感谢!对不起,如果你不介意的话,最后一个有点相关的问题。假设我想在工作簿的多个工作表上运行我的函数,替换当前使用的函数。要使用此函数快速替换工作簿中的所有单元格,我可以执行“全部替换”。但是,当我执行此操作时,工作簿中使用此函数的所有单元格都会返回我打开的当前工作表中单元格的值。有没有一种方法可以使函数在使用它的每个单元格中独立工作?我看不出这种行为(但我没有你的确切公式)。假设输入范围与公式单元格在同一张表上,它应该返回一个适当的值,因为我们总是在
rng.Parent
的上下文中进行计算。你能分享你的工作簿吗(或者至少是一些能说明你有问题的东西)?对不起,我不知道如何分享我的工作簿,但这里有一个基本的例子:在工作表1中,我在a列中有数字,在B1单元格中,我有引用a列的函数。在工作表2中,我有完全相同的设置。在工作表2中,我找到>替换,并替换工作簿中的所有内容。我用新函数替换旧函数。所以在第二张表中,单元格B1的计算值是正确的,但当我进入第1张表时,单元格B1的值与第二张表中的值相同,而不是计算它自己的值。这有意义吗?!