Skip to content

Instantly share code, notes, and snippets.

@mtagda

mtagda/main.go Secret

Last active November 16, 2020 21:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mtagda/5a2390c102ed219e6c13a8f198ddb3b2 to your computer and use it in GitHub Desktop.
Save mtagda/5a2390c102ed219e6c13a8f198ddb3b2 to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"math"
"gonum.org/v1/gonum/stat/distuv"
)
const (
alpha = 0.05 // Significance level of the statistical test. Must be greater than 0.
)
func normPPF(x float64) float64 {
// Create a normal distribution
dist := distuv.Normal{
Mu: 0,
Sigma: 1,
}
// Calculate PPF (percent point function) which is the inverse of the cumulative distribution function (CDF) of the normal distribution
return dist.Quantile(x)
}
func sign(x float64) float64 {
// Get sign of a float (-1 if negative, 0 if 0 and 1 if positive)
if x < 0 {
return -1
} else if x == 0 {
return 0
} else {
return 1
}
}
func getValuesFromMap(mapping map[float64]int) []int {
// Get values from map
v := make([]int, 0, len(mapping))
for _, value := range mapping {
v = append(v, value)
}
return v
}
func countUniqueValues(arr []float64) map[float64]int {
//Create a counter for values in an array
dict := make(map[float64]int)
for _, num := range arr {
dict[num] = dict[num] + 1
}
return dict
}
// Mann-Kendall test ...
func MKtest(series []float64, alpha float64) (string, error) {
// Pseudocode see https://up-rs-esp.github.io/mkt/
// Get length of time series
n := len(series)
if n <= 1 {
return "none", fmt.Errorf("Only one data point provided. No trend present.")
}
// Calculate S which is the number of positive differences minus the number of negative differences
// If S is a positive number, observations obtained later in time tend to be larger than observations made earlier.
// If S is a negative number, then observations made later in time tend to be smaller than observations made earlier.
s := 0.0
for k := 0; k < n-1; k++ {
for j := k + 1; j < n; j++ {
s += sign(series[j] - series[k])
}
}
// Calculate unique observations
uniq := countUniqueValues(series)
g := len(uniq)
varS, z := 0.0, 0.0
// Calculate var(s)
if g == n {
// No tie case
varS = float64((n * (n - 1) * (2*n + 5))) / 18
} else {
// Ties in data case
tp := getValuesFromMap(uniq)
sumExpression := 0
for i, _ := range tp {
sumExpression += tp[i] * (tp[i] - 1) * (2*tp[i] + 5)
}
varS = float64((n*(n-1)*(2*n+5) + sumExpression)) / 18
}
// Compute the Mann-Kendall test statistic Z
if s > 0 {
z = float64(s-1) / math.Sqrt(float64(varS))
} else if s == 0 {
z = 0
} else if s < 0 {
z = float64(s+1) / math.Sqrt(float64(varS))
}
// Calculate the h value
h := math.Abs(z) > normPPF(1-alpha/2)
// Decide if we see trend in data (default: "none" which means no trend)
trend := "none"
if z < 0 && h == true {
trend = "decreasing"
} else if z > 0 && h == true {
trend = "increasing"
}
return trend, nil
}
func main() {
trend := []float64{444.86422078,
442.03048113, 440.55915912, 430.17507337, 422.61711963,
407.65081784, 404.60838605, 427.39930304, 433.34926929,
437.40598052, 443.03321287, 442.89414329, 459.83770303,
474.06537847, 454.72264949, 457.77727018, 455.55108019,
459.04705359, 463.45009691, 461.49681143, 460.22330154,
473.81861709, 479.75938288, 484.73895342, 481.59747338,
480.1184492, 484.00466265, 479.56595413, 471.3967811,
459.25010965, 460.79176371, 468.67765623, 491.64362735,
492.22523428, 525.15392318, 533.23089641, 543.54148873,
559.76465954, 573.59507116, 556.52540265, 551.68180752,
526.80761937, 559.05410939, 572.5692387, 574.80430187,
577.85605905, 596.31760844, 601.41447938, 609.78789718,
602.2355323, 592.05365229, 608.66472746, 576.52373187,
601.68847731, 660.22029991}
trendIndication, err := MKtest(trend, alpha)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(trendIndication)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment