【问题标题】:AverageIf function results in "#VALUE!"AverageIf 函数的结果是“#VALUE!”
【发布时间】:2017-02-22 06:38:09
【问题描述】:

这是我的数据截图。

Dim dBT As Object 'global dictionary

Sub buttonpresscount()

    'constants for column positions
    Const COL_BLOCK As Long = 1
    Const COL_TRIAL As Long = 2
    Const COL_ACT As Long = 7
    Const COL_AOI As Long = 8
    Const COL_RT As Long = 16
    Const COL_FT As Long = 17

    Dim rng As Range, lastrow As Long, sht As Worksheet
    Dim d, r As Long, k, resBT()

    Set sht = Worksheets("full test")
    lastrow = sht.Cells(Rows.Count, 3).End(xlUp).Row
    Set dBT = CreateObject("scripting.dictionary")

    Set rng = sht.Range("B7:T" & lastrow)

    d = rng.Value  'get the data into an array

    ReDim resBT(1 To UBound(d), 1 To 1) 'resize the array which will
                                        '  be placed in ColT

    'get unique combinations of Block and Trial and pressedcounts for each
    For r = 1 To UBound(d, 1)
        k = d(r, COL_BLOCK) & "|" & d(r, COL_TRIAL) 'create key
        dBT(k) = dBT(k) + IIf(d(r, COL_ACT) <> "", 1, 0)
    Next r

    'populate array with appropriate counts for each row
    For r = 1 To UBound(d, 1)
        k = d(r, 1) & "|" & d(r, 2)   'create key
        resBT(r, 1) = dBT(k)         'get the count
    Next r

    'place array to sheet
    sht.Range("T7").Resize(UBound(resBT, 1), 1) = resBT

    'clear dictionary
    dBT.RemoveAll

'count AOI entries
 For r = 1 To UBound(d, 1)
        k = d(r, COL_BLOCK) & "|" & d(r, COL_TRIAL) 'create key
        If resBT(r, 1) = 1 Then    'only proceed with trials with 1 button press
        dBT(k) = dBT(k) + IIf(d(r, COL_AOI) = "AOI Entry", 1, 0)    'get count
        Else: dBT(k) = ""
        End If
    Next r

    'populate array with appropriate counts for each row
    For r = 1 To UBound(d, 1)
        k = d(r, 1) & "|" & d(r, 2)   'create key
        resBT(r, 1) = dBT(k)          'get the count
    Next r

    'place array to sheet
    sht.Range("U7").Resize(UBound(resBT, 1), 1) = resBT

Call createsummarytable
Call PopSummaryAOI(dBT)

dBT.RemoveAll

'retrieve and print reaction times to data summary sheet
   For r = 1 To UBound(d, 1)
        If resBT(r, 1) <> "" Then 'if buttonpresscount = 1 and AOI count exists
        k = d(r, COL_BLOCK) & "|" & d(r, COL_TRIAL) 'create key
        dBT(k) = d(r, COL_RT)
        End If
    Next r

 'Populate array with last row reaction time for each trial
    For r = 1 To UBound(d, 1)
        k = d(r, 1) & "|" & d(r, 2)   'create key
        resBT(r, 1) = dBT(k)          'get the count
    Next r

Call PopSummaryRT(dBT)

dBT.RemoveAll

'work out avg fixation time per trial
For r = 1 To UBound(d, 1)
    If resBT(r, 1) <> "" Then
    k = d(r, COL_BLOCK) & "|" & d(r, COL_TRIAL) 'create key
    dBT(k) = Application.AverageIf(d(r, COL_FT), (d(r, COL_AOI) = "AOI Entry"))
    End If
Next r

'populate array
For r = 1 To UBound(d, 1)
        k = d(r, 1) & "|" & d(r, 2)   'create key
        resBT(r, 1) = dBT(k)          'get the count
Next r

Call PopSummaryFT(dBT)

End Sub

参考上述宏,以下代码行旨在计算每个 dict(key) 列 R 中的值的平均值(阅读:每次试验):

For r = 1 To UBound(d, 1)
    If resBT(r, 1) <> "" Then
    k = d(r, COL_BLOCK) & "|" & d(r, COL_TRIAL) 'create key
    dBT(k) = Application.AverageIf(d(r, COL_FT), (d(r, COL_AOI) = "AOI Entry"))
    End If
Next r

这导致#VALUE! 被打印在相关单元格中,而不是预期的数字。

截图:

这是什么原因造成的?编写此公式的正确方法是什么?

【问题讨论】:

  • 如果该值是True(如果r+6 行中的值,则您的行dBT(k) = Application.AverageIf(d(r, COL_FT), (d(r, COL_AOI) = "AOI Entry")) 正在取位于行r+6、列R 的单元格中的值的平均值,第 I 列是 "AOI Entry") 或 False(如果行中的值 r+6,第 I 列不是 "AOI Entry")。这总是会产生除以零错误(因为你没有 TrueFalse 值),所以这无疑会产生你的 #VALUE 错误。通常对多个值执行平均 - 您要对哪些值进行平均?
  • 您是否只是想生成与=AVERAGEIFS($R:$R,$I:$I,"AOI Entry",$B:$B,$B7,$C:$C,$C7) 的公式(例如在单元格 T7 中)等效的 VBA?
  • @YowE3K 我想要 R 列中的值的平均值,但只有那些在 I 列中具有“AOI 条目”的行中的值。你是说问题在于我如何定义纳入标准?
  • @YowE3K 我不希望单元格中的公式,我希望它被键入到字典对象,字典键应该定义哪些行包含在平均值中。
  • 如果您想计算 VBA 中的平均值(不参考工作表中的范围),我认为您需要手动计算它(即,将您需要的值相加并除以如何许多值求和)或使用Application.AverageIfs,使用与在 Excel 中使用的参数相似的参数 - 但每个范围(例如 R:R)必须是一维数组 - 即你不能只使用d 数组的一个“切片”,您必须将“切片”创建为一个单独的数组。

标签: excel vba average


【解决方案1】:

您当前的线路问题

dBT(k) = Application.AverageIf(d(r, COL_FT), (d(r, COL_AOI) = "AOI Entry"))

是因为您尝试取单个值的平均值,并且仅当该值为 TrueFalse 时。例如。当r 为 1 时,您的代码等于

dbt("Block 1|Trial, 8") = Application.AverageIf(-2484, ("" = "AOI Entry"))

dbt("Block 1|Trial, 8") = Application.AverageIf(-2484, False)

由于平均范围内的值(即值-2484)均不符合条件(即False),因此该函数尝试将匹配值的总和(即0)除以计数匹配值(即0)并输出错误。

同样,当r为2时,代码等于

dbt("Block 1|Trial, 1") = Application.AverageIf(31, ("AOI Entry" = "AOI Entry"))

dbt("Block 1|Trial, 1") = Application.AverageIf(31, True)

同样,31 不等于 True,您最终会尝试将 0 除以 0。


如果你使用了一个公式,你会得到一个答案(虽然不是一个有意义的答案)

dBT(k) = Application.AverageIf(d(r, COL_AOI), "AOI Entry", d(r, COL_FT))

如果d(r, COL_AOI)(根据标准进行测试的范围)匹配AOI Entry"(标准),则这将是d(r, COL_FT)(平均范围)的总和。 (对单个数字求和有点毫无意义,但它仍然会这样做。)但是,当d(r, COL_AOI) 不是"AOI Entry" 时,这仍然会导致除以零错误,并且在这种情况下会给出毫无意义的答案什么时候起作用。


要获得有意义的平均值,您需要将值的总和除以值的计数。您的代码设置方式不便于使用 Excel 的内置函数计算总和和计数,因此您需要自己计算总和和计数。

在下面的代码中,我添加了两个字典(一个名为 Sums,另一个名为 Cnts)来跟踪这些数字。然后可以通过将Sums(k) 除以Cnts(k) 轻松得出平均值。

我还冒昧地将您的变量 k 更改为一个数组。您当前的代码在至少 8 个位置计算密钥,所以我将其更改为计算一次,然后在其他位置使用相同的值。

Dim dBT As Object 'global dictionary

Sub buttonpresscount()
    Dim Sums As Object
    Dim Cnts As Object

    'constants for column positions
    Const COL_BLOCK As Long = 1
    Const COL_TRIAL As Long = 2
    Const COL_ACT As Long = 7
    Const COL_AOI As Long = 8
    Const COL_RT As Long = 16
    Const COL_FT As Long = 17

    Dim rng As Range, lastrow As Long, sht As Worksheet
    Dim d, r As Long, resBT()
    Dim k() As String

    Set sht = Worksheets("full test")
    lastrow = sht.Cells(Rows.Count, 3).End(xlUp).Row
    Set dBT = CreateObject("scripting.dictionary")
    Set Sums = CreateObject("scripting.dictionary")
    Set Cnts = CreateObject("scripting.dictionary")

    Set rng = sht.Range("B7:T" & lastrow)

    d = rng.Value  'get the data into an array

    ReDim resBT(1 To UBound(d), 1 To 1) 'resize the array which will
                                        '  be placed in ColT
    ReDim k(1 To UBound(d, 1)) As String

    'get unique combinations of Block and Trial and pressedcounts for each
    For r = 1 To UBound(d, 1)
        'Calculate the key once, then it can be used in every other loop
        k(r) = d(r, COL_BLOCK) & "|" & d(r, COL_TRIAL) 'create key
        dBT(k(r)) = dBT(k(r)) + IIf(d(r, COL_ACT) <> "", 1, 0)
    Next r

    'populate array with appropriate counts for each row
    For r = 1 To UBound(d, 1)
        resBT(r, 1) = dBT(k(r))        'get the count
    Next r

    'place array to sheet
    sht.Range("T7").Resize(UBound(resBT, 1), 1) = resBT

    'clear dictionary
    dBT.RemoveAll

    'count AOI entries
    For r = 1 To UBound(d, 1)
        If resBT(r, 1) = 1 Then    'only proceed with trials with 1 button press
            If d(r, COL_AOI) = "AOI Entry" Then
                dBT(k(r)) = dBT(k(r)) + 1     'get count
                Cnts(k(r)) = Cnts(k(r)) + 1   'get count
                Sums(k(r)) = Sums(k(r)) + d(r, COL_FT)   'sum column R
            End If
        Else
            dBT(k(r)) = ""
        End If
    Next r

    'populate array with appropriate counts for each row
    For r = 1 To UBound(d, 1)
        resBT(r, 1) = dBT(k(r))          'get the count
    Next r

    'place array to sheet
    sht.Range("U7").Resize(UBound(resBT, 1), 1) = resBT

    createsummarytable
    PopSummaryAOI dBT

    dBT.RemoveAll

    'retrieve and print reaction times to data summary sheet
    For r = 1 To UBound(d, 1)
        If resBT(r, 1) <> "" Then 'if buttonpresscount = 1 and AOI count exists
            dBT(k(r)) = d(r, COL_RT)
        End If
    Next r

    'Populate array with last row reaction time for each trial
    For r = 1 To UBound(d, 1)
        resBT(r, 1) = dBT(k(r))          'get the count
    Next r

    PopSummaryRT dBT

    dBT.RemoveAll

    'work out avg fixation time per trial
    For r = 1 To UBound(d, 1)
        If resBT(r, 1) <> "" Then
            If Cnts(k(r)) < 1 Then
                'Error if no results
                dBT(k(r)) = CVErr(xlErrDiv0)
            Else
                'Determine average
                dBT(k(r)) = Sums(k(r)) / Cnts(k(r))
            End If
        End If
    Next r

    'populate array
    For r = 1 To UBound(d, 1)
        resBT(r, 1) = dBT(k(r))          'get the count
    Next r

    PopSummaryFT dBT

End Sub

【讨论】:

  • 很好的答案并且代码有效。如果没有 AOI 条目,我希望它仍然打印 0,所以我将 If d(r, COL_AOI) = "AOI Entry" Then dBT(k(r)) = dBT(k(r)) + 1 'get count Cnts(k(r)) = Cnts(k(r)) + 1 'get count Sums(k(r)) = Sums(k(r)) + d(r, COL_FT) 'sum column R 更改为 If d(r, COL_AOI) = "AOI Entry" Then dBT(k(r)) = dBT(k(r)) + 1 'get count Else dBT(k(r)) = dBT(k(r)) + 0 'get count,现在许多平均值都是负数。不知道为什么。
  • nvm 我想我修好了。我为平均 'Else 'Cnts(k(r)) = Cnts(k(r)) + 0 'get count 'Sums(k(r)) = Sums(k(r)) + d(r, COL_FT) 'sum column R 添加了一个 else 声明,我认为这是原因。没有它也可以正常工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-26
  • 1970-01-01
  • 2010-12-10
相关资源
最近更新 更多