Skip to content

Commit 8b2d11e

Browse files
committed
Moved Gaussian to stats package and improved tests
1 parent 3a605f9 commit 8b2d11e

File tree

3 files changed

+108
-16
lines changed

3 files changed

+108
-16
lines changed

stats/cdf.go

+28-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,30 @@ package stats
22

33
import "math"
44

5-
// NormalCDF calculates the Cumulative Distribution Function for the
6-
// standardized normal distribution
5+
// NormGaussAt returns the probability of x in a standardized normal
6+
// distribution
7+
func NormGaussAt(x float64) float64 {
8+
return GaussAt(x, 0, 1)
9+
}
10+
11+
// GaussAt returns the probability of x for the normal distribution with mean mu
12+
// and standard deviation sigma
13+
func GaussAt(x, mu, sigma float64) float64 {
14+
multiplier := 1.0 / (sigma * math.Sqrt(2*math.Pi))
15+
expPart := math.Exp((-1.0 * math.Pow(x-mu, 2.0)) / (2 * sigma * sigma))
16+
return multiplier * expPart
17+
}
18+
19+
// NormalCDF calculates the Cumulative Distribution function for the
20+
// standardized normal distribution.
721
func NormalCDF(x float64) float64 {
8-
return (1.0 / 2.0) * (1 + math.Erf(x/math.Sqrt2))
22+
return CDF(x, 0, 1)
23+
}
24+
25+
// CDF calculates the cumulative distribution function for any standard
26+
// distribution.
27+
func CDF(x, mu, sigma float64) float64 {
28+
return 0.5 * (1 + math.Erf((x-mu)/(sigma*math.Sqrt2)))
929
}
1030

1131
// InverseCDF calculates the Inverse Cumulative Distribution Function for the
@@ -15,14 +35,18 @@ func NormalCDF(x float64) float64 {
1535
func InverseCDF(p float64) float64 {
1636
var r, x, pp, dp float64
1737

18-
dp = p - 0.5
38+
if p > 1 || p < 0 {
39+
return math.NaN()
40+
}
41+
1942
switch p {
2043
case 1.0:
2144
return math.Inf(1)
2245
case 0.0:
2346
return math.Inf(-1)
2447
}
2548

49+
dp = p - 0.5
2650
if math.Abs(dp) <= 0.425 {
2751
x = small(dp)
2852
return x

stats/cdf_test.go

+78-10
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,83 @@ import (
55
"testing"
66
)
77

8+
const (
9+
ErrTolerance = 1e-6
10+
)
11+
12+
func TestNormGaussAt(t *testing.T) {
13+
in := []float64{
14+
0,
15+
1,
16+
-1,
17+
3.7,
18+
-3.7,
19+
}
20+
21+
// These values were generated using dnorm on http://www.r-fiddle.org/
22+
out := []float64{
23+
0.3989422804,
24+
0.24197072452,
25+
0.24197072452,
26+
0.00042478027055,
27+
0.00042478027055,
28+
}
29+
30+
for i, v := range in {
31+
result := NormGaussAt(v)
32+
if math.Abs(result-out[i]) > ErrTolerance {
33+
t.Error("Expected", out[i], "got", result, "for", v)
34+
}
35+
}
36+
}
37+
38+
func TestGaussAt(t *testing.T) {
39+
in := []float64{
40+
20,
41+
25,
42+
15,
43+
38.5,
44+
1.5,
45+
}
46+
47+
// These values were generated using dnorm on http://www.r-fiddle.org/
48+
out := []float64{
49+
0.07978845608,
50+
0.048394144904,
51+
0.048394144904,
52+
8.495605411e-5,
53+
8.495605411e-5,
54+
}
55+
56+
for i, v := range in {
57+
result := GaussAt(v, 20, 5)
58+
if math.Abs(result-out[i]) > ErrTolerance {
59+
t.Error("Expected", out[i], "got", result, "for", v)
60+
}
61+
}
62+
}
63+
864
func TestNormalCDF(t *testing.T) {
965
in := []float64{
1066
math.Inf(1),
1167
0,
1268
math.Inf(-1),
13-
InverseCDF(0.1),
14-
InverseCDF(0.9),
69+
1,
70+
-1,
71+
2,
72+
-2,
1573
InverseCDF(0.86),
1674
}
1775

76+
// These results were calculated using pnorm(x) on www.r-fiddle.org
1877
out := []float64{
1978
1,
2079
0.5,
2180
0,
22-
0.1,
23-
0.9,
81+
0.841344746069,
82+
0.158655253931,
83+
0.977249868052,
84+
0.022750131948,
2485
0.86,
2586
}
2687

@@ -31,7 +92,7 @@ func TestNormalCDF(t *testing.T) {
3192
for i := range in {
3293
o := NormalCDF(in[i])
3394

34-
if math.Abs(o-out[i]) > errTolerance {
95+
if math.Abs(o-out[i]) > ErrTolerance {
3596
t.Errorf("NormalCDF(%f) == %f, want %f", in[i], o, out[i])
3697
}
3798
}
@@ -42,17 +103,24 @@ func TestInverseCDF(t *testing.T) {
42103
1,
43104
0.5,
44105
0,
45-
NormalCDF(1),
46-
NormalCDF(-1),
106+
0.2,
107+
0.3,
108+
0.9,
109+
-1,
110+
2,
47111
NormalCDF(4),
48112
}
49113

114+
// These results were calculated using qnorm(x) on www.r-fiddle.org
50115
out := []float64{
51116
math.Inf(1),
52117
0,
53118
math.Inf(-1),
54-
1,
55-
-1,
119+
-0.84162123357,
120+
-0.52440051271,
121+
1.2815515655,
122+
math.NaN(),
123+
math.NaN(),
56124
4,
57125
}
58126

@@ -63,7 +131,7 @@ func TestInverseCDF(t *testing.T) {
63131
for i := range in {
64132
o := InverseCDF(in[i])
65133

66-
if math.Abs(o-out[i]) > errTolerance {
134+
if math.Abs(o-out[i]) > ErrTolerance {
67135
t.Errorf("InverseCDF(%f) == %f, want %f", in[i], o, out[i])
68136
}
69137
}

gauss.go stats/gauss.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package trueskill
1+
package stats
22

33
import (
44
"fmt"
@@ -61,7 +61,7 @@ func (g *Gaussian) GetConservativeEstimate() float64 {
6161

6262
// String can be used to print a Gaussian
6363
func (g *Gaussian) String() string {
64-
text := fmt.Sprintf("μ=%1.4f, σ=%1.4f", g.GetMu(), g.GetSigma())
64+
text := fmt.Sprintf("μ=%2.4f, σ=%2.4f", g.GetMu(), g.GetSigma())
6565
return text
6666
}
6767

0 commit comments

Comments
 (0)