简体   繁体   English

加权平均 IFS UDF

[英]Weighted Average IFS UDF

I am trying to make a Weighted Average Ifs function with the capability of having up to three ifs.我正在尝试使加权平均 Ifs function 具有最多三个 ifs 的能力。 as it stands, the function only works when the optionality is removed and all arguments are filled by the user.就目前而言,function 仅在删除可选性并且所有 arguments 均由用户填写时才有效。 When the function is run #Value.当 function 运行时#Value。 is returned when optional arguments are not filled. optional arguments 未填写时返回。 here is the complete code in question.这是有问题的完整代码。

Function WeightedAvgIfs(ByVal values As Range, ByVal weights As Range, _
ByVal ConditionRange1 As Range, ByVal Condition1 As String, _
Optional ByVal ConditionRange2 As Range = Nothing, Optional ByVal Condition2 As String = "=ZZZ", _
Optional ByVal ConditionRange3 As Range = Nothing, Optional ByVal Condition3 As String = "=ZZZ") As Double

Dim ValuesArray(), WeightsArray(), Condition1Array(), Condition2Array(), Condition3Array() As Variant
Dim i As Long
Dim dsum As Double
Dim StringOperator As String
Dim Condition As Variant

ValuesArray = Range(values.Address(1, 1, xlA1, 1))
WeightsArray = Range(weights.Address(1, 1, xlA1, 1))
Condition1Array = Range(ConditionRange1.Address(1, 1, xlA1, 1))
Condition2Array = Range(ConditionRange2.Address(1, 1, xlA1, 1))
Condition2Array = Range(ConditionRange3.Address(1, 1, xlA1, 1))

'Condition 1
For i = LBound(ValuesArray) To UBound(ValuesArray)

    Select Case Left(Condition1, 2)
        Case Is = "<="
            StringOperator = "<="
            Condition = Val(Mid(Condition1, 3, Len(Condition1)))
        Case Is = ">="
            StringOperator = ">="
            Condition = Val(Mid(Condition1, 3, Len(Condition1)))
        Case Is = "<>"
            StringOperator = "<>"
            If IsNumeric(Mid(Condition1, 3, Len(Condition1))) And Not IsEmpty(Condition1) Then
                Condition = Val(Mid(Condition1, 3, Len(Condition1)))
            Else
                Condition = UCase(Mid(Condition1, 3, Len(Condition1)))
            End If
        
        Case Else
            Select Case Left(Condition1, 1)
                Case Is = "<"
                    StringOperator = "<"
                    Condition = Val(Mid(Condition1, 2, Len(Condition1)))
                Case Is = ">"
                    StringOperator = ">"
                    Condition = Val(Mid(Condition1, 2, Len(Condition1)))
                Case Is = "="
                    StringOperator = "="
                    If IsNumeric(Mid(Condition1, 2, Len(Condition1))) And Not IsEmpty(Condition1) Then
                        Condition = Val(Mid(Condition1, 2, Len(Condition1)))
                    Else
                        Condition = UCase(Mid(Condition1, 2, Len(Condition1)))
                    End If
            End Select
    End Select
    Select Case StringOperator
        Case Is = ">="
            If Condition1Array(i, 1) < Condition Then
                ValuesArray(i, 1) = 0
                WeightsArray(i, 1) = 0
            End If
        Case Is = ">"
            If Condition1Array(i, 1) <= Condition Then
                ValuesArray(i, 1) = 0
                WeightsArray(i, 1) = 0
            End If
        Case Is = "<="
            If Condition1Array(i, 1) > Condition Then
                ValuesArray(i, 1) = 0
                WeightsArray(i, 1) = 0
            End If
        Case Is = "<"
            If Condition1Array(i, 1) >= Condition Then
                ValuesArray(i, 1) = 0
                WeightsArray(i, 1) = 0
            End If
        Case Else
            Select Case StringOperator
                Case Is = "="
                    If IsNumeric(Condition1Array(i, 1)) And Not IsEmpty(Condition1Array(i, 1)) Then
                        If Val(Condition1Array(i, 1)) <> Condition Then
                            ValuesArray(i, 1) = 0
                            WeightsArray(i, 1) = 0
                        End If
                    Else
                        If UCase(CStr(Condition1Array(i, 1))) <> Condition Then
                            ValuesArray(i, 1) = 0
                            WeightsArray(i, 1) = 0
                        End If
                    End If
                Case Is = "<>"
                    If IsNumeric(Condition1Array(i, 1)) And Not IsEmpty(Condition1Array(i, 1)) Then
                        If Val(Condition1Array(i, 1)) = Condition Then
                            ValuesArray(i, 1) = 0
                            WeightsArray(i, 1) = 0
                        End If
                    Else
                        If UCase(CStr(Condition1Array(i, 1))) = Condition Then
                            ValuesArray(i, 1) = 0
                            WeightsArray(i, 1) = 0
                        End If
                    End If
            End Select
    End Select
    
Next i

If ConditionRange2 Is Nothing Then
    GoTo FinalCalc
End If

'Condition 2

For i = LBound(ValuesArray) To UBound(ValuesArray)

    Select Case Left(Condition2, 2)
        Case Is = "<="
            StringOperator = "<="
            Condition = Val(Mid(Condition2, 3, Len(Condition2)))
        Case Is = ">="
            StringOperator = ">="
            Condition = Val(Mid(Condition2, 3, Len(Condition2)))
        Case Is = "<>"
            StringOperator = "<>"
            If IsNumeric(Mid(Condition2, 3, Len(Condition2))) And Not IsEmpty(Condition2) Then
                Condition = Val(Mid(Condition2, 3, Len(Condition2)))
            Else
                Condition = UCase(Mid(Condition2, 3, Len(Condition2)))
            End If
        
        Case Else
            Select Case Left(Condition2, 1)
                Case Is = "<"
                    StringOperator = "<"
                    Condition = Val(Mid(Condition2, 2, Len(Condition2)))
                Case Is = ">"
                    StringOperator = ">"
                    Condition = Val(Mid(Condition2, 2, Len(Condition2)))
                Case Is = "="
                    StringOperator = "="
                    If IsNumeric(Mid(Condition2, 2, Len(Condition2))) And Not IsEmpty(Condition2) Then
                        Condition = Val(Mid(Condition2, 2, Len(Condition2)))
                    Else
                        Condition = UCase(Mid(Condition2, 2, Len(Condition2)))
                    End If
            End Select
    End Select
    Select Case StringOperator
        Case Is = ">="
            If Condition2Array(i, 1) < Condition Then
                ValuesArray(i, 1) = 0
                WeightsArray(i, 1) = 0
            End If
        Case Is = ">"
            If Condition2Array(i, 1) <= Condition Then
                ValuesArray(i, 1) = 0
                WeightsArray(i, 1) = 0
            End If
        Case Is = "<="
            If Condition2Array(i, 1) > Condition Then
                ValuesArray(i, 1) = 0
                WeightsArray(i, 1) = 0
            End If
        Case Is = "<"
            If Condition2Array(i, 1) >= Condition Then
                ValuesArray(i, 1) = 0
                WeightsArray(i, 1) = 0
            End If
        Case Else
            Select Case StringOperator
                Case Is = "="
                    If IsNumeric(Condition2Array(i, 1)) And Not IsEmpty(Condition2Array(i, 1)) Then
                        If Val(Condition2Array(i, 1)) <> Condition Then
                            ValuesArray(i, 1) = 0
                            WeightsArray(i, 1) = 0
                        End If
                    Else
                        If UCase(CStr(Condition2Array(i, 1))) <> Condition Then
                            ValuesArray(i, 1) = 0
                            WeightsArray(i, 1) = 0
                        End If
                    End If
                Case Is = "<>"
                    If IsNumeric(Condition2Array(i, 1)) And Not IsEmpty(Condition2Array(i, 1)) Then
                        If Val(Condition2Array(i, 1)) = Condition Then
                            ValuesArray(i, 1) = 0
                            WeightsArray(i, 1) = 0
                        End If
                    Else
                        If UCase(CStr(Condition2Array(i, 1))) = Condition Then
                            ValuesArray(i, 1) = 0
                            WeightsArray(i, 1) = 0
                        End If
                    End If
            End Select
    End Select
    
Next i
If ConditionRange3 Is Nothing Then
GoTo FinalCalc
End If

'Condition 3
For i = LBound(ValuesArray) To UBound(ValuesArray)

    Select Case Left(Condition3, 2)
        Case Is = "<="
            StringOperator = "<="
            Condition = Val(Mid(Condition3, 3, Len(Condition3)))
        Case Is = ">="
            StringOperator = ">="
            Condition = Val(Mid(Condition3, 3, Len(Condition3)))
        Case Is = "<>"
            StringOperator = "<>"
            If IsNumeric(Mid(Condition3, 3, Len(Condition3))) And Not IsEmpty(Condition3) Then
                Condition = Val(Mid(Condition3, 3, Len(Condition3)))
            Else
                Condition = UCase(Mid(Condition3, 3, Len(Condition3)))
            End If
        
        Case Else
            Select Case Left(Condition3, 1)
                Case Is = "<"
                    StringOperator = "<"
                    Condition = Val(Mid(Condition3, 2, Len(Condition3)))
                Case Is = ">"
                    StringOperator = ">"
                    Condition = Val(Mid(Condition3, 2, Len(Condition3)))
                Case Is = "="
                    StringOperator = "="
                    If IsNumeric(Mid(Condition3, 2, Len(Condition3))) And Not IsEmpty(Condition3) Then
                        Condition = Val(Mid(Condition3, 2, Len(Condition3)))
                    Else
                        Condition = UCase(Mid(Condition3, 2, Len(Condition3)))
                    End If
            End Select
    End Select
    Select Case StringOperator
        Case Is = ">="
            If Condition3Array(i, 1) < Condition Then
                ValuesArray(i, 1) = 0
                WeightsArray(i, 1) = 0
            End If
        Case Is = ">"
            If Condition3Array(i, 1) <= Condition Then
                ValuesArray(i, 1) = 0
                WeightsArray(i, 1) = 0
            End If
        Case Is = "<="
            If Condition3Array(i, 1) > Condition Then
                ValuesArray(i, 1) = 0
                WeightsArray(i, 1) = 0
            End If
        Case Is = "<"
            If Condition3Array(i, 1) >= Condition Then
                ValuesArray(i, 1) = 0
                WeightsArray(i, 1) = 0
            End If
        Case Else
            Select Case StringOperator
                Case Is = "="
                    If IsNumeric(Condition3Array(i, 1)) And Not IsEmpty(Condition3Array(i, 1)) Then
                        If Val(Condition3Array(i, 1)) <> Condition Then
                            ValuesArray(i, 1) = 0
                            WeightsArray(i, 1) = 0
                        End If
                    Else
                        If UCase(CStr(Condition3Array(i, 1))) <> Condition Then
                            ValuesArray(i, 1) = 0
                            WeightsArray(i, 1) = 0
                        End If
                    End If
                Case Is = "<>"
                    If IsNumeric(Condition3Array(i, 1)) And Not IsEmpty(Condition3Array(i, 1)) Then
                        If Val(Condition3Array(i, 1)) = Condition Then
                            ValuesArray(i, 1) = 0
                            WeightsArray(i, 1) = 0
                        End If
                    Else
                        If UCase(CStr(Condition3Array(i, 1))) = Condition Then
                            ValuesArray(i, 1) = 0
                            WeightsArray(i, 1) = 0
                        End If
                    End If
            End Select
    End Select
    
Next i

FinalCalc:

dsum = Application.WorksheetFunction.Sum(WeightsArray)

For i = LBound(WeightsArray) To UBound(WeightsArray)
    WeightsArray(i, 1) = WeightsArray(i, 1) / dsum
Next i
For i = LBound(ValuesArray) To UBound(ValuesArray)
    ValuesArray(i, 1) = ValuesArray(i, 1) * WeightsArray(i, 1)
Next i

WeightedAvgIfs = Application.WorksheetFunction.Sum(ValuesArray)
End Function

Here's a paramarray version with some other optimizations.这是具有其他一些优化的 paramarray 版本。 I did skip your IsNumeric/Empty checks but general idea is there...我确实跳过了你的 IsNumeric/Empty 检查,但总体思路是......

'opts = 1 or more pairs of Range, Condition values
Function WeightedAvgIfs(ByVal values As Range, ByVal weights As Range, ParamArray opts()) As Double

    Dim ValuesArray(), WeightsArray(), CondArray() As Variant
    Dim i As Long, opt As Long
    Dim dsum As Double
    Dim StringOperator As String
    Dim Cond As Variant, op As String, condVal, bOK As Boolean
    
    ValuesArray = values.Value
    WeightsArray = weights.Value
    'loop over any condition range + value pairs provided
    For opt = LBound(opts) To UBound(opts) Step 2
        CondArray = opts(opt).Value          'read the criteria range values
        Cond = Trim(opts(opt + 1))           'read the criteria
        op = Left(Cond, 2)                   'extract the criteria operator
        op = IIf(op = "<=" Or op = ">=" Or op = "<>", op, Left(op, 1))
        Cond = Trim(Right(Cond, Len(Cond) - Len(op))) 'extract the criteria value
        For i = LBound(ValuesArray) To UBound(ValuesArray)
            If ValuesArray(i, 1) <> 0 And WeightsArray(i, 1) <> 0 Then 'check not already excluded
                bOK = False
                condVal = CondArray(i, 1)
                Select Case op
                    Case "<=": bOK = (condVal <= Cond)
                    Case "<": bOK = (condVal < Cond)
                    Case ">=": bOK = (condVal >= Cond)
                    Case ">": bOK = (condVal > Cond)
                    Case "=": bOK = (condVal = Cond)
                    Case "<>": bOK = (condVal <> Cond)
                End Select
                If Not bOK Then 'filtered out in this run?
                    ValuesArray(i, 1) = 0
                    WeightsArray(i, 1) = 0
                End If
            End If
        Next i
    Next opt
    
    dsum = Application.WorksheetFunction.Sum(WeightsArray)
    For i = LBound(ValuesArray) To UBound(ValuesArray)
        ValuesArray(i, 1) = ValuesArray(i, 1) * (WeightsArray(i, 1) / dsum)
    Next i
    WeightedAvgIfs = Application.WorksheetFunction.Sum(ValuesArray)
End Function

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM