loading...

16 December 2024
Interpolation
Excel VBA UDF (with Rebalance) · Excel VBA Linear UDF

Here is my interpretation of using Interpolation for Missing Buckets rolled up into an Excel VBA UDF. This UDF Interpolation Function provides Linear Interpolation of missing buckets with the ability to avoid inflation by rebalancing the values if required. It also allows the use of a Fixed Start value and a Fixed End value with two coefficients to adjust these values further, again, if required. Copy & Paste this Code into any Standard Code Module Back · Purchase

 
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
Public Function Interpolation(ByVal Known_ys As Range, Optional ByVal FixedStart As Boolean = False, Optional ByVal FixedEnd As Boolean = False, Optional ByVal StartCoefficient As Double = 1, Optional ByVal EndCoefficient As Double = 1, Optional Rebalance As Boolean = True) As Variant
    Dim Anchor As Boolean
    Dim Share As Double
    Dim TempShare As Double
    Dim SUM(1)
    Dim X As Long
    Dim Y As Long
    Dim Z As Long
    Dim FirstIndex As Long
    Dim NextIndex As Long
    Dim LastFirstIndex As Long
    Dim LastNextIndex As Long
    Dim V As Variant
  
    V = Known_ys
    FirstIndex = 1
    NextIndex = 1
    Anchor = False
     
    For X = 1 To UBound(V, 2)
        If V(1, X) <> Empty Then V(1, X) = Val(V(1, X))
        If V(1, X) <= 0 Then
            V(1, X) = Empty
        End If
    Next X
    SUM(0) = Application.WorksheetFunction.SUM(V)
     
    For X = 1 To UBound(V, 2)
               
        If V(1, X) <= 0 Then
            If Not Anchor Then
                If NextIndex > FirstIndex Then FirstIndex = NextIndex
                NextIndex = X
                Anchor = True
            End If
        End If
               
        If V(1, X) > 0 Then
            Z = Z + 1
            If Anchor Then
                NextIndex = X
                Anchor = False
            Else
                FirstIndex = X
            End If
     
        End If
               
        If V(1, X) > 0 And NextIndex > FirstIndex Then
            Anchor = False
            If V(1, NextIndex) > V(1, FirstIndex) Then
                If V(1, FirstIndex) <= 0 Then
                    Share = (V(1, NextIndex) - V(1, FirstIndex)) / ((NextIndex - FirstIndex) + 1)
                Else
                    Share = (V(1, NextIndex) - V(1, FirstIndex)) / ((NextIndex - FirstIndex))
                End If
            Else
                Share = (V(1, FirstIndex) - V(1, NextIndex)) / ((NextIndex - FirstIndex))
            End If
     
            TempShare = V(1, FirstIndex)
            If V(1, FirstIndex) <= 0 Then
     
                For Y = FirstIndex To NextIndex - 1
                    If FixedStart Then
                        V(1, Y) = V(1, NextIndex) * StartCoefficient
                    Else
                        V(1, Y) = (V(1, Y) + TempShare + Share) * StartCoefficient
                        TempShare = TempShare + Share 'V(1, Y)
                    End If
                Next Y
     
            ElseIf V(1, NextIndex) > V(1, FirstIndex) Then
                For Y = FirstIndex + 1 To NextIndex - 1
                    V(1, Y) = (V(1, Y) + TempShare + Share)
                    TempShare = TempShare + Share 'V(1, Y)
                Next Y
            Else
                TempShare = V(1, NextIndex)
                For Y = NextIndex - 1 To FirstIndex + 1 Step -1
                    V(1, Y) = (V(1, Y) + TempShare + Share)
                    TempShare = TempShare + Share 'V(1, Y)
                Next Y
            End If
     
        Else
            TempShare = 0
            NextIndex = X
            LastFirstIndex = FirstIndex
            LastNextIndex = NextIndex
        End If
     
    Next X
     
    If V(1, NextIndex) <= 0 Then
        Share = (V(1, FirstIndex) - V(1, NextIndex)) / ((NextIndex - FirstIndex + 1))
        TempShare = 0
        For Y = FirstIndex + 1 To NextIndex
            If FixedEnd Then
                TempShare = V(1, FirstIndex) * EndCoefficient
                If TempShare < 0 Then TempShare = 0
                V(1, Y) = TempShare
            Else
                TempShare = (V(1, Y - 1) - Share) * EndCoefficient
                If TempShare < 0 Then TempShare = 0
                V(1, Y) = TempShare
            End If
        Next Y
    End If
    SUM(1) = Application.WorksheetFunction.SUM(V)
         
    If Rebalance Then
        For X = 1 To UBound(V, 2)
            V(1, X) = V(1, X) / SUM(1) * SUM(0)
        Next X
    End If
     
    Interpolation = V
End Function
 
 
Public Function Interpolation(ByVal Known_ys As Range, Optional ByVal FixedStart As Boolean = False, Optional ByVal FixedEnd As Boolean = False, Optional ByVal StartCoefficient As Double = 1, Optional ByVal EndCoefficient As Double = 1, Optional Rebalance As Boolean = True) As Variant
    Dim Anchor As Boolean
    Dim Share As Double
    Dim TempShare As Double
    Dim SUM(1)
    Dim X As Long
    Dim Y As Long
    Dim Z As Long
    Dim FirstIndex As Long
    Dim NextIndex As Long
    Dim LastFirstIndex As Long
    Dim LastNextIndex As Long
    Dim V As Variant
 
    V = Known_ys
    FirstIndex = 1
    NextIndex = 1
    Anchor = False
    
    For X = 1 To UBound(V, 2)
        If V(1, X) <> Empty Then V(1, X) = Val(V(1, X))
        If V(1, X) <= 0 Then
            V(1, X) = Empty
        End If
    Next X
    SUM(0) = Application.WorksheetFunction.SUM(V)
    
    For X = 1 To UBound(V, 2)
              
        If V(1, X) <= 0 Then
            If Not Anchor Then
                If NextIndex > FirstIndex Then FirstIndex = NextIndex
                NextIndex = X
                Anchor = True
            End If
        End If
              
        If V(1, X) > 0 Then
            Z = Z + 1
            If Anchor Then
                NextIndex = X
                Anchor = False
            Else
                FirstIndex = X
            End If
    
        End If
              
        If V(1, X) > 0 And NextIndex > FirstIndex Then
            Anchor = False
            If V(1, NextIndex) > V(1, FirstIndex) Then
                If V(1, FirstIndex) <= 0 Then
                    Share = (V(1, NextIndex) - V(1, FirstIndex)) / ((NextIndex - FirstIndex) + 1)
                Else
                    Share = (V(1, NextIndex) - V(1, FirstIndex)) / ((NextIndex - FirstIndex))
                End If
            Else
                Share = (V(1, FirstIndex) - V(1, NextIndex)) / ((NextIndex - FirstIndex))
            End If
    
            TempShare = V(1, FirstIndex)
            If V(1, FirstIndex) <= 0 Then
    
                For Y = FirstIndex To NextIndex - 1
                    If FixedStart Then
                        V(1, Y) = V(1, NextIndex) * StartCoefficient
                    Else
                        V(1, Y) = (V(1, Y) + TempShare + Share) * StartCoefficient
                        TempShare = TempShare + Share 'V(1, Y)
                    End If
                Next Y
    
            ElseIf V(1, NextIndex) > V(1, FirstIndex) Then
                For Y = FirstIndex + 1 To NextIndex - 1
                    V(1, Y) = (V(1, Y) + TempShare + Share)
                    TempShare = TempShare + Share 'V(1, Y)
                Next Y
            Else
                TempShare = V(1, NextIndex)
                For Y = NextIndex - 1 To FirstIndex + 1 Step -1
                    V(1, Y) = (V(1, Y) + TempShare + Share)
                    TempShare = TempShare + Share 'V(1, Y)
                Next Y
            End If
    
        Else
            TempShare = 0
            NextIndex = X
            LastFirstIndex = FirstIndex
            LastNextIndex = NextIndex
        End If
    
    Next X
    
    If V(1, NextIndex) <= 0 Then
        Share = (V(1, FirstIndex) - V(1, NextIndex)) / ((NextIndex - FirstIndex + 1))
        TempShare = 0
        For Y = FirstIndex + 1 To NextIndex
            If FixedEnd Then
                TempShare = V(1, FirstIndex) * EndCoefficient
                If TempShare < 0 Then TempShare = 0
                V(1, Y) = TempShare
            Else
                TempShare = (V(1, Y - 1) - Share) * EndCoefficient
                If TempShare < 0 Then TempShare = 0
                V(1, Y) = TempShare
            End If
        Next Y
    End If
    SUM(1) = Application.WorksheetFunction.SUM(V)
        
    If Rebalance Then
        For X = 1 To UBound(V, 2)
            V(1, X) = V(1, X) / SUM(1) * SUM(0)
        Next X
    End If
    
    Interpolation = V
End Function

Function information is added to the Macro Table in the 'ThisWorkbook' Sheet giving you intellisense about the Named Arguments in the 'fx' (Formula Box) in Excel. Copy the Code below into the 'ThisWorkbook' Sheet Code Module:

 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
Private Sub Workbook_Open()
 
    Dim FunctionName As String
    Dim FunctionDesc As String
    Dim ArgDesc(1 To 6) As String
 
    FunctionName = "Interpolation"
    FunctionDesc = "Performs Linear Interpolation on Known_ys with the ability to fix the Start & End values and rebalance any inflation"
    ArgDesc(1) = "Provide the Known_ys that will be used for Linear Interpolation"
    ArgDesc(2) = "Optional: Choose whether to use a Fixed Start value as opposed to Interpolation.  True = yes, False = no, default = False"
    ArgDesc(3) = "Optional: Choose whether to use a Fixed End value as opposed to Interpolation.  True = yes, False = no, default = False"
    ArgDesc(4) = "Optional: Start Coefficient used to multiply the Start value, default = 1"
    ArgDesc(5) = "Optional: End Coefficient used to multiply the End value, default = 1"
    ArgDesc(6) = "Optional: Rebalance used to evenly distribute the inflation due to Interpolation, default = True"
    Application.MacroOptions Macro:=FunctionName, Description:=FunctionDesc, Category:=vbNullString, ArgumentDescriptions:=ArgDesc
 
End Sub
 
 
Private Sub Workbook_Open()

    Dim FunctionName As String
    Dim FunctionDesc As String
    Dim ArgDesc(1 To 6) As String

    FunctionName = "Interpolation"
    FunctionDesc = "Performs Linear Interpolation on Known_ys with the ability to fix the Start & End values and rebalance any inflation"
    ArgDesc(1) = "Provide the Known_ys that will be used for Linear Interpolation"
    ArgDesc(2) = "Optional: Choose whether to use a Fixed Start value as opposed to Interpolation.  True = yes, False = no, default = False"
    ArgDesc(3) = "Optional: Choose whether to use a Fixed End value as opposed to Interpolation.  True = yes, False = no, default = False"
    ArgDesc(4) = "Optional: Start Coefficient used to multiply the Start value, default = 1"
    ArgDesc(5) = "Optional: End Coefficient used to multiply the End value, default = 1"
    ArgDesc(6) = "Optional: Rebalance used to evenly distribute the inflation due to Interpolation, default = True"
    Application.MacroOptions Macro:=FunctionName, Description:=FunctionDesc, Category:=vbNullString, ArgumentDescriptions:=ArgDesc

End Sub

Use the UDF like this in any Worksheet in Excel for a range of any size (Example shown Range := $D28:$R28, 15 values):

 
1
2
3
4
5
' Interpolation (unbalanced)
=Interpolation($D28:$R28,FALSE,FALSE,1,1,FALSE)
 
' Interpolation (rebalanced)
=Interpolation($D28:$R28)
 
 
' Interpolation (unbalanced)
=Interpolation($D28:$R28,FALSE,FALSE,1,1,FALSE)

' Interpolation (rebalanced)
=Interpolation($D28:$R28)

Here are the Actuals values used for this demonstration detailing the Interpolation results for both unbalanced and rebalanced values. Please Note: zeros can be zero (0) or blank - the algorithm treats them as similar. Also negatives are removed by the algorithm and treated as zero / blank. I will underline the Interpolated values for the unbalanced series so that you can clearly see the values that have been Interpolated for the missing buckets. One last point is that for this algorithm we cannot start with a zero (0) hence we always divide by 'Gap' + 1 to ensure a non-zero start i.e. for 4 missing buckets and a first value of 8, we would Interpolate: 1.6, 3.2, 4.8, 6.4 and then 8, not: 0, 2, 4, 6, 8 or: 1, 2, 4, 6, 8

Series 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Actuals 0 0 4 -1 8 11 3 6 9 7
Interpolation (unbalanced) 1.3 2.7 4 5.3 6.7 8 11 3 4.5 6 7.5 9 7 4.7 2.3
Interpolation (rebalanced) 0.8 1.5 2.3 3.1 3.9 4.6 6.4 1.7 2.6 3.5 4.3 5.2 4.0 2.7 1.3

Chart demonstrating the VBA UDF Linear Interpolation Function on a range of 15 values, Fixed Start := False, Fixed End := False, Start Coefficient := 1, End Coefficient := 1, Rebalance := False. The Actuals series SUM to 48 and the Interpolation series SUM to 83 (unbalanced and with inflation)

Chart demonstrating the VBA UDF Linear Interpolation Function on a range of 15 values, Fixed Start := False, Fixed End := False, Start Coefficient := 1, End Coefficient := 1, Rebalance := True. The Actuals series and the Interpolation series both SUM to 48 (rebalanced without inflation)





Here is another Linear Interpolation using An Excel VBA UDF. This UDF provides Linear Interpolation of missing buckets by coearsing the first and last buckets to 1 if they are missing and/or they are negative or zero (0). This Interpolation routine uses a Slope and Intercept calculation to Interpolate between any Series of 'Known_ys' against any Series of 'Known_xs', meaning it can fit the Interpolation values for sequential or disparate periods, for example, given Known_ys of '2, 0 and 4' and 'Known_xs' periods of '7, 12 and 20' the period '12' of the 'Known_xs' for the value '0' will be interpolated to '2.77'. This Interpolation routine will inflate the final values as it does not rebalance the weightings of the values following Interpolation. Copy & Paste this Code into any Standard Code Module Back · Purchase

 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
Public Function LinearInterpolate(ByVal Known_ys As Range, ByVal Known_xs As Range) As Variant
  
    Dim First As Long
    Dim Last As Long
    Dim X As Long
    Dim Y As Long
    Dim SizeOfYS As Long
    Dim Slope As Double
    Dim Intercept As Double
    Dim YS As Variant
    Dim XS As Variant
 
    YS = Known_ys
    SizeOfYS = UBound(YS, 2)
    XS = Known_xs
 
    For X = 1 To SizeOfYS
        If YS(1, X) <> Empty Then YS(1, X) = Val(YS(1, X))
        If YS(1, X) <= 0 Then
            YS(1, X) = Empty
        End If
        If YS(1, 1) <= 0 Then YS(1, 1) = 1
        If YS(1, SizeOfYS) <= 0 Then YS(1, SizeOfYS) = 1
    Next X
 
    First = 0
    For X = 1 To SizeOfYS
        If YS(1, X) <= 0 Then
            If First = 0 Then First = X - 1
        ElseIf YS(1, X) > 0 And First > 0 Then
            Last = X
            For Y = First + 1 To X - 1
                Slope = (YS(1, Last) - YS(1, First)) / (XS(1, Last) - XS(1, First))
                Intercept = YS(1, First) - Slope * XS(1, First)
                YS(1, Y) = (Slope * XS(1, Y) + Intercept)
            Next Y
            First = 0
        End If
    Next X
 
    LinearInterpolate = YS
 
End Function
 
 
Public Function LinearInterpolate(ByVal Known_ys As Range, ByVal Known_xs As Range) As Variant
 
    Dim First As Long
    Dim Last As Long
    Dim X As Long
    Dim Y As Long
    Dim SizeOfYS As Long
    Dim Slope As Double
    Dim Intercept As Double
    Dim YS As Variant
    Dim XS As Variant

    YS = Known_ys
    SizeOfYS = UBound(YS, 2)
    XS = Known_xs

    For X = 1 To SizeOfYS
        If YS(1, X) <> Empty Then YS(1, X) = Val(YS(1, X))
        If YS(1, X) <= 0 Then
            YS(1, X) = Empty
        End If
        If YS(1, 1) <= 0 Then YS(1, 1) = 1
        If YS(1, SizeOfYS) <= 0 Then YS(1, SizeOfYS) = 1
    Next X

    First = 0
    For X = 1 To SizeOfYS
        If YS(1, X) <= 0 Then
            If First = 0 Then First = X - 1
        ElseIf YS(1, X) > 0 And First > 0 Then
            Last = X
            For Y = First + 1 To X - 1
                Slope = (YS(1, Last) - YS(1, First)) / (XS(1, Last) - XS(1, First))
                Intercept = YS(1, First) - Slope * XS(1, First)
                YS(1, Y) = (Slope * XS(1, Y) + Intercept)
            Next Y
            First = 0
        End If
    Next X

    LinearInterpolate = YS

End Function

Function information is added to the Macro Table in the 'ThisWorkbook' Sheet giving you intellisense about the Named Arguments in the 'fx' (Formula Box) in Excel. Copy the Code below into the 'ThisWorkbook' Sheet Code Module:

 
01
02
03
04
05
06
07
08
09
10
11
12
13
Private Sub Workbook_Open()
 
    Dim FunctionName As String
    Dim FunctionDesc As String
    Dim ArgDesc(1 To 2) As String
 
    FunctionName = "LinearInterpolate"
    FunctionDesc = "Performs Linear Interpolation on Known_ys with the ability to Interpolate any period.  Start and End are set to 1 if negative/zero/blank"
    ArgDesc(1) = "Provide the Known_ys that will be used for the Linear Interpolation, typically the Actuals"
    ArgDesc(2) = "Provide the Known_xs that will be used for Linear Interpolation, typically the Periods"
    Application.MacroOptions Macro:=FunctionName, Description:=FunctionDesc, Category:=vbNullString, ArgumentDescriptions:=ArgDesc
 
End Sub
 
 
Private Sub Workbook_Open()

    Dim FunctionName As String
    Dim FunctionDesc As String
    Dim ArgDesc(1 To 2) As String

    FunctionName = "LinearInterpolate"
    FunctionDesc = "Performs Linear Interpolation on Known_ys with the ability to Interpolate any period.  Start and End are set to 1 if negative/zero/blank"
    ArgDesc(1) = "Provide the Known_ys that will be used for the Linear Interpolation, typically the Actuals"
    ArgDesc(2) = "Provide the Known_xs that will be used for Linear Interpolation, typically the Periods"
    Application.MacroOptions Macro:=FunctionName, Description:=FunctionDesc, Category:=vbNullString, ArgumentDescriptions:=ArgDesc

End Sub

Use the UDF like this in any Worksheet in Excel for a range of any size:

 
1
2
' Known_ys = Actuals, '$D28:$S28' and Knowyn_xs = Periods (sequential), '$D27:$S27'
=LinearInterpolate($D28:$S28,$D27:$S27)
 
 
' Known_ys = Actuals, '$D28:$S28' and Knowyn_xs = Periods (sequential), '$D27:$S27' 
=LinearInterpolate($D28:$S28,$D27:$S27)

Here are the Actuals values used for this demonstration detailing the Interpolation results on Sequential Time Periods. I will underline the Interpolated values for the series so that you can clearly see the values that have been Interpolated for the missing buckets

Series 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Actuals 2 3 2 2 3 3 6 2 2 11 12
Interpolate 2 2.5 3 2 2 2.5 3 3 4.5 6 2 2 5.0 8.0 11 12

Chart demonstrating the VBA UDF Linear Interpolate Function on a range of 16 sequential values

Here are the Actuals values used for this demonstration detailing the Interpolation results on Disparate Time Periods. I will underline the Interpolated values for the series so that you can clearly see the values that have been Interpolated for the missing buckets

Series 7 12 20
Actuals 2 4
Interpolate 2 2.77 4

Chart demonstrating the VBA UDF Linear Interpolate Function on a range of 3 disparate values