Skip to content

Instantly share code, notes, and snippets.

@Cirzen
Last active August 29, 2015 14:25
Show Gist options
  • Save Cirzen/4364d59742fba9b5ce5c to your computer and use it in GitHub Desktop.
Save Cirzen/4364d59742fba9b5ce5c to your computer and use it in GitHub Desktop.
SumProduct, Exponential arrays and weighted averages
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