… or other irregular cyclic data.
Another Eng-Tips question asked how to approximate ice area over a freeze-thaw cycle using a function based on a sine or cosine curve. The screen-shot below shows three alternatives:
Using built in Excel functions requires a separate function for the freeze and thaw part of the cycle:
=(IF(DOY<DOY_FS,-1,IF(DOY>DOY_FE,1,SIN((DOY-F_Mid)/F_Days*PI())))+1)/2*A_max
=(IF(DOY<DOY_MS,1,IF(DOY>DOY_ME,-1,COS((DOY-DOY_MS)/M_Days*PI())))+1)/2*A_max
I have incorporated these in a short user defined function (UDF), which returns a column array of the full data range. (See Using Array Functions and UDFs if you are not familiar with array functions).
Function ScaleSin(DatRange As Variant, Outx As Variant)
Dim Inc0x As Double, Inc0y As Double, Inc1x As Double, Inc1y As Double
Dim Dec0x As Double, Dec0y As Double, Dec1x As Double, Dec1y As Double
Dim NumX As Long, i As Long, ResA() As Double, OutXA() As Double, DX As Double, DY As Double
Dim Pi As Double
Pi = Atn(1) * 4
DatRange = DatRange.Value2
Inc0x = DatRange(1, 1)
Inc0y = DatRange(1, 2)
Inc1x = DatRange(2, 1)
Inc1y = DatRange(2, 2)
Dec0x = DatRange(3, 1)
Dec0y = DatRange(3, 2)
Dec1x = DatRange(4, 1)
Dec1y = DatRange(4, 2)
Outx = Outx.Value2
NumX = UBound(Outx)
ReDim ResA(1 To NumX, 1 To 1)
ReDim OutXA(1 To NumX, 1 To 1)
i = 1
Do While i <= NumX
Do While Outx(i, 1) < Inc0x
ResA(i, 1) = Inc0y
i = i + 1
Loop
DX = Inc1x - Inc0x
DY = Inc1y - Inc0y
Do While Outx(i, 1) < Inc1x
OutXA(i, 1) = (Outx(i, 1) - Inc0x) / DX * Pi - Pi / 2
ResA(i, 1) = Inc0y + DY * (Sin(OutXA(i, 1)) + 1) / 2
i = i + 1
Loop
DX = Dec0x - Inc1x
DY = Dec0y - Inc1y
Do While Outx(i, 1) < Dec0x
ResA(i, 1) = Inc1y + DY * (Outx(i, 1) - Inc1x) / DX
i = i + 1
Loop
DX = Dec1x - Dec0x
DY = Dec0y - Dec1y
Do While Outx(i, 1) < Dec1x
OutXA(i, 1) = (Outx(i, 1) - Dec0x) / DX * Pi + Pi / 2
ResA(i, 1) = Dec1y + DY * (Sin(OutXA(i, 1)) + 1) / 2
i = i + 1
Loop
Do While i <= NumX
ResA(i, 1) = Dec1y
i = i + 1
Loop
i = i + 1
Loop
ScaleSin = ResA
End Function
An alternative approach suggested at the Eng-Tips discussion is to use a Sigmoid function of the form:
I have written another UDF to return such a function:
Function Sigmoid(xA As Variant, Optional a As Double = 1, Optional b As Double = 1, Optional c As Double = 1, _
Optional d As Double = 1, Optional f As Double = -5, Optional t As Double = 0)
Dim Z As Double, NumX As Long, x As Double, i As Long, ResA() As Double
xA = xA.Value2
If IsArray(xA) Then
NumX = UBound(xA)
Else
NumX = 1
End If
ReDim ResA(1 To NumX, 1 To 1)
For i = 1 To NumX
If NumX = 1 Then
x = d * (xA - f)
Else
x = d * (xA(i, 1) - f)
End If
If x >= 0 Then
ResA(i, 1) = a / (b + c * Exp(-x)) + t
Else
Z = Exp(x)
ResA(i, 1) = a * Z / (b + c * Z) + t
End If
Next i
Sigmoid = ResA
End Function
The sigmoid function also returns an array, but must be entered separately for the freeze and thaw part of the cycle.
Examples of the use of both functions applied to freeze-thaw data are given in the download file: Freeze-sin.xlsb
As usual, the download file includes full open-source code.

Pingback: #Excel Super Links #79 – shared by David Hager | Excel For You