Vba UDF在任何地方都返回相同的值
我试图在vba中编写移动平均值代码,但下面的代码在任何地方都返回相同的值Vba UDF在任何地方都返回相同的值,vba,excel,with-statement,udf,Vba,Excel,With Statement,Udf,我试图在vba中编写移动平均值代码,但下面的代码在任何地方都返回相同的值 Function trial1(a As Integer) As Variant Application.Volatile Dim rng As Range Set rng = Range(Cells(ActiveCell.Row, 2), Cells(ActiveCell.Row - a + 1, 2)) trial1 = (Application.Sum(rng)) * (1 / a)
Function trial1(a As Integer) As Variant
Application.Volatile
Dim rng As Range
Set rng = Range(Cells(ActiveCell.Row, 2), Cells(ActiveCell.Row - a + 1, 2))
trial1 = (Application.Sum(rng)) * (1 / a)
End Function
不属于UDF,因为它会更改。有时,它甚至不在同一工作表上
如果需要引用工作表中自定义UDF函数所在的单元格,请使用以下方法。可以使用来明确标识工作表(并避免进一步混淆)
您已应用ª方法,但未明确指定父工作表,因此允许将范围平均为默认值
通过返回a的结果和一些数学计算平均值。工作表的一个命令可能会返回相同的结果,但空白单元格的处理方式会有所不同
trial1 = Application.Average(rng)
当整个工作簿中的任何内容发生更改时,而不仅仅是当影响其结果的内容发生更改时,Volatile函数都会重新计算。我认为Application.ActiveCell不是您应该在这里使用的。 假设“a”是子集的大小,并且数据集是右侧的1列,则Application.ThisCell更合适。 此外,我将简单地使用“WorksheetFunction.Average”而不是“Application.Sum”,并添加“Application.Volatile”,以便在工作表上发生更新时重新计算平均值 因此,解决您的问题的一个方法是:
Public Function Trial1(a As Integer) As Variant
Application.Volatile
Trial1 = WorksheetFunction.Average(Application.ThisCell(1, 2).Resize(a))
End Function
这里的另一个解决方案是使用Control/Shift/Enter输入的数组公式:
Public Function MovAvg(dataset As Range, subsetSize As Integer)
Dim result(), subset As Range, i As Long
ReDim result(1 To dataset.Rows.count, 1 To 1)
Set subset = dataset.Resize(subsetSize)
For i = 1 To dataset.Rows.count
result(i, 1) = WorksheetFunction.Average(subset.offset(i - 1))
Next
MovAvg = result
End Function
要使用此数组函数,请执行以下操作:
- 选择将写入所有结果的范围(应为数据集的大小)
- 类型“=MovAvg(A1:A100,2)”,其中A1:A100是数据源,2是子集的大小
- 按Ctrl+Shift+Enter键
- 范围C2命名为
MovingAverageSize
- 从B3和B3以下存储的数据
- 移动平均结果存储在数据右侧的一列中
- 如果数据小于MovingAverageSize,则SUM函数会相应调整
- 发生任何计算错误,结果为零
- 每次MovingAverageSize更改值时,它都会触发一个子系统来更新公式(代码放在工作表对象中,而不是普通模块中)
- 或者,您可以更改代码,将MovingAverage放在MovingAverageSize的同一列中,这样您就可以在彼此旁边比较一些不同的大小
- 对于一个给定数字的UDF来说,计算移动平均值对我来说有点奇怪。如果要在工作表中使用此自定义项,我相信您会将其放在现有数据的旁边,如果您想更改平均金额范围的大小,您可以手动更新它们吗
假设您可以指定一个范围“<强>移动平均值> /强>”来存储计算平均值的范围的大小,以及现有数据的右边的平均量,请考虑如下:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Count = 1 Then
If Target.Address = ThisWorkbook.Names("MovingAverageSize").RefersToRange.Address Then UpdateMovingAverage Target
End If
End Sub
Private Sub UpdateMovingAverage(ByRef Target As Range)
Dim oRngData As Range, oRng As Range, lSize As Long, lStartRow As Long
Debug.Print "UpdateMovingAverage(" & Target.Address & ")"
If IsNumeric(Target) Then
lSize = CLng(Target.Value)
If lSize <= 0 Then
MsgBox "Moving Average Window Size cannot be zero or less!", vbExclamation + vbOKOnly
Else
' Top Data range is "B3"
Set oRngData = Target.Parent.Cells(3, "B") ' <-- Change to match your top data cell
lStartRow = oRngData.Row
' Set the Range to last row on the same column
Set oRngData = Range(oRngData, Cells(Rows.Count, oRngData.Column).End(xlUp))
Application.EnableEvents = False
For Each oRng In oRngData
If (oRng.Row - lSize) < lStartRow Then
oRng.Offset(0, 1).FormulaR1C1 = "=iferror(sum(R[" & lStartRow - oRng.Row & "]C[-1]:RC[-1])/MovingAverageSize,0)"
Else
oRng.Offset(0, 1).FormulaR1C1 = "=iferror(sum(R[" & 1 - lSize & "]C[-1]:RC[-1])/MovingAverageSize,0)"
End If
Next
Application.EnableEvents = True
Set oRngData = Nothing
End If
End If
End Sub
选项显式
私有子工作表_更改(ByVal目标作为范围)
如果Target.Count=1,则
如果Target.Address=thiswook.Names(“MovingAverageSize”).referestorange.Address,则UpdateMovingAverage目标
如果结束
端接头
私有子UpdateMovingAverage(ByRef目标作为范围)
变暗或变宽为范围,或变宽为范围,变大为长,变小为长
Debug.Print“UpdateMovingAverage(&Target.Address&')”
如果是数字(目标),则
lSize=CLng(目标值)
如果lSizeUDF仅应在作为参数传递时访问范围。
此外,您应该消除Application.Volatile,因为(1)您的计算是确定性的,而不是易变的,(2)每当输入范围中的任何单元格发生变化时,Excel将自动重新计算您的UDF,(3)因为UDF中的“Volatile”属性会使模型非常慢,因此在不必要时应避免使用它。
因此,对于移动平均线,正确的公式是:
Public Function SpecialMovingAverage(Rng as Excel.Range) As Double
Dim denominator as Integer
denominator = Rng.Cells.Count
if Denominator = 0 then SpecialMovingAverage = 0: exit function
' write your special moving average logic below
SpecialMovingAverage = WorksheetFunction.Average(Rng)
End Function
注:我在两条评论后更改了答案,因为我最初没有看到问题是在移动平均线之后(可能是在我的答案之后更改了问题,或者我最初没有达到UDF规定的目标)。我相信
trial1()函数位于一个或多个单元格中,作为公式的一部分或单独存在
您希望在用户更改工作表上的任何单元格时重新计算这些单元格
为此,您需要识别发生更改的单元格。此单元格不是由
A.ActiveCell-因为计算开始时光标所在的单元格;它可能在任何地方,但不在被改变的细胞上
B.Application.ThisCell-因为它返回调用用户定义函数的单元格,而不是已更改的单元格
发生更改的单元格将传递给工作表的更改事件。该事件由一个Range类型的参数触发,即已更改的范围。您可以使用该参数来标识已更改的单元格,并将其传递给trial1(),可能是通过一个全局变量(是的,我知道)
我在工作表中尝试了这一点,效果很好,所以请告诉我您的结果。那么这里的问题是什么?我的代码似乎不起作用。它为所有单元格返回相同的值。如果我删除application.volatile行,我必须手动按F2键并输入每个单元格以获得正确答案。我认为原因是函数无法识别动态范围中存在的变化,但不知道如何修复它。我测试了它,但它没有在任何地方返回相同的值,它似乎做了它应该做的事情。您的问题的问题是,我们不确切知道您想要什么,我们应该参考您的代码来了解您正在做什么。但是你的代码可能是错的。从我看来:1。选择一个单元格,触发调用函数Tria的宏
Public Function SpecialMovingAverage(Rng as Excel.Range) As Double
Dim denominator as Integer
denominator = Rng.Cells.Count
if Denominator = 0 then SpecialMovingAverage = 0: exit function
' write your special moving average logic below
SpecialMovingAverage = WorksheetFunction.Average(Rng)
End Function