Last active
August 29, 2015 14:25
-
-
Save Cirzen/4364d59742fba9b5ce5c to your computer and use it in GitHub Desktop.
SumProduct, Exponential arrays and weighted averages
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Function Get-ArrayProduct { | |
<# | |
.SYNOPSIS | |
Returns the multiplication product of two equally sized numerical arrays. | |
e.g (1,2,3) * (4,5,6) causes (1*4,2*5,3*6) and returns (4,10,18) | |
#> | |
[CmdLetBinding()] | |
Param( | |
[Double[]] | |
$Array1, | |
[Double[]] | |
$Array2 | |
) | |
$Resultant = New-Object system.double[] (@($Array2).Length) | |
If (@($Array1).Length -ne @($Array2).Length) | |
{ | |
Throw "Arrays must be the same length to multiply" | |
} | |
$length = (@($Array1).Length) -1 | |
ForEach ($i in 0..$length) | |
{ | |
$Resultant[$i] = $Array1[$i] * $Array2[$i] | |
} | |
Return $Resultant | |
} | |
Function Get-SumProduct { | |
<# | |
.SYNOPSIS | |
Emulates the behaviour of Microsoft Excel's SUMPRODUCT function and returns the | |
Sum of the Products of a number of equally sized arrays. | |
Boolean values are coerced to 0/1 allowing conditional summation, as in Excel. | |
e.g. | |
Sum((1,1,2) * (4,5,6) * ($false,$true,$false)) = Sum(1*4*0,1*5*1,2*6*0) = Sum(0,5,0) = 5 | |
#> | |
[CmdLetBinding()] | |
Param( | |
[Object[]] | |
$Arrays | |
) | |
If (@($Arrays).Count -eq 0) {Return} | |
If (@($Arrays).Count -eq 1) {Return $Arrays|Measure-Object -Sum|Select -ExpandProperty Sum} | |
$Resultant = @(1) * $Arrays[0].Length | |
For ($i=1;$i -le @($Arrays).Count;$i++) | |
{ | |
$Resultant = Get-ArrayProduct -Array1 $Resultant -Array2 $Arrays[$i-1] | |
} | |
Return $Resultant|Measure-Object -Sum|Select -ExpandProperty Sum | |
} | |
Function New-ExponentialArray | |
{ | |
<# | |
.SYNOPSIS | |
Creates an array of exponential values according to the function | |
f(x) = (a.x)^(e^t) | |
Where: | |
a = Correction factor | |
x = each entry in the array | |
e = Euler's constant (2.71828...) | |
t = Tension - the strength of the curve and deviation from a linear response. | |
The correction factor is used to Normalise the resulting output array such that the sum of the array is | |
(close to) 1. That is, the area under the curve has unity area. This may help for instance in calculating | |
weighted averages. It is calculated via the indefinite integral of f(x). The overall total may not sum to | |
1 in most cases as the calculation is effectively the sum of the series of rectangles of width (1/ArraySize) | |
along the curve (the Reimann sum). The error may be significant for small array sizes or extreme tension | |
values. Additionally, extreme tension values (exceeding +/- 4) may cause bounding errors for the double | |
precision floating point numbers causing some values to lock to zero or plus infinity. Try more moderate | |
tension values, reducing the size of the array, or with/without normalisation to resolve. | |
The Tension value provides the value to which e is raised. Positive values deviate the curve downwards | |
from a straight line (concave), negative values deviate the curve upwards from a straight line (convex). | |
A value of 0 results in a linear step response. | |
./| | |
. /,| , = Positive tension | |
. / ,| | |
. / ,| . = Negative tension | |
. / , | | |
/ , | | |
. / , | | |
/, | | |
--------- | |
#> | |
[CmdLetBinding()] | |
Param( | |
[Parameter(Mandatory=$True,Position=1,ValueFromPipeline=$True)] | |
[uint32] | |
$Size, | |
[Parameter(Mandatory=$False,Position=2)] | |
[float] | |
$Tension=1.0, | |
[switch] | |
[Alias("Normalized")] | |
$Normalised | |
) | |
Begin | |
{ | |
$Resultant = New-Object System.Double[] $Size | |
[double]$e_Tension_pos = [math]::Exp($Tension) | |
[double]$e_Tension_neg = [math]::Exp(-$Tension) | |
If ($Normalised) | |
{ | |
# ( ( ( 1+EXP(Tension) ) /Size ) ^ EXP(-Tension) )/ Size | |
[double]$CorrectionFactor = ([Math]::Pow((1+$e_Tension_pos)/$Size,$e_Tension_neg))/$Size | |
} | |
Else | |
{ | |
$CorrectionFactor = 1 | |
} | |
Write-Verbose "Correction Factor: $CorrectionFactor" | |
} | |
Process | |
{ | |
} | |
End | |
{ | |
For ($i=0;$i -lt $Size;$i++) | |
{ | |
$Resultant[$i] = [math]::Pow(($CorrectionFactor * ($i + 0.5)), $e_Tension_pos) | |
} | |
Return $Resultant | |
} | |
} | |
Function Get-ExponentiallyWeightedAverage | |
{ | |
<# | |
.SYNOPSIS | |
#> | |
[CmdLetBinding()] | |
Param( | |
[Parameter(Mandatory=$True,Position=0,ValueFromPipeline=$True)] | |
[Double[]] | |
$Data, | |
[Parameter(Mandatory=$False,Position=1,ValueFromPipelineByPropertyName=$True)] | |
[ValidateRange(-5,5)] | |
[Alias("Tension")] | |
[float] | |
$ExponentialStrength = 0, #Default to linear falloff | |
[Switch] | |
[Alias("UseNormalization","Normalised","Normalized")] | |
$UseNormalisation | |
) | |
$Size = @($Data).Count | |
$ExponentialArray = New-ExponentialArray -Size $Size -Tension $ExponentialStrength -Normalised:$UseNormalisation | |
If ($UseNormalisation) | |
{ | |
Get-SumProduct -Arrays $Data, $ExponentialArray | |
} | |
Else | |
{ | |
(Get-SumProduct -Arrays $Data, $ExponentialArray) / ($ExponentialArray|Measure-Object -Sum|Select-Object -ExpandProperty Sum) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment