-
-
Save mtagda/5a2390c102ed219e6c13a8f198ddb3b2 to your computer and use it in GitHub Desktop.
This file contains 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
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