Skip to content
This repository was archived by the owner on Dec 22, 2018. It is now read-only.

Commit 606c62f

Browse files
committed
Add CircularMean
1 parent 8d52a49 commit 606c62f

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

stat.go

+25
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,31 @@ func ChiSquare(obs, exp []float64) float64 {
125125
return result
126126
}
127127

128+
// CircularMean returns the circular mean of the dataset.
129+
// atan2(\sum_i w_i * sin(alpha_i), \sum_i w_i * cos(alpha_i))
130+
// If weights is nil then all of the weights are 1. If weights is not nil, then
131+
// len(x) must equal len(weights).
132+
func CircularMean(x, weights []float64) float64 {
133+
if weights != nil && len(x) != len(weights) {
134+
panic("stat: slice length mismatch")
135+
}
136+
137+
var aX, aY float64
138+
if weights != nil {
139+
for i, v := range x {
140+
aX += weights[i] * math.Cos(v)
141+
aY += weights[i] * math.Sin(v)
142+
}
143+
} else {
144+
for _, v := range x {
145+
aX += math.Cos(v)
146+
aY += math.Sin(v)
147+
}
148+
}
149+
150+
return math.Atan2(aY, aX)
151+
}
152+
128153
// Correlation returns the weighted correlation between the samples of x and y
129154
// with the given means.
130155
// sum_i {w_i (x_i - meanX) * (y_i - meanY)} / (stdX * stdY)

stat_test.go

+46
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,52 @@ import (
1313
"github.com/gonum/floats"
1414
)
1515

16+
func ExampleCircularMean() {
17+
x := []float64{0, 0.25 * math.Pi, 0.75 * math.Pi}
18+
weights := []float64{1, 2, 2.5}
19+
cmean := CircularMean(x, weights)
20+
21+
fmt.Printf("The circular mean is %.5f.\n", cmean)
22+
// Output:
23+
// The circular mean is 1.37037.
24+
}
25+
26+
func TestCircularMean(t *testing.T) {
27+
for i, test := range []struct {
28+
x []float64
29+
wts []float64
30+
ans float64
31+
}{
32+
// Values compared against scipy.
33+
{
34+
x: []float64{0, 2 * math.Pi},
35+
ans: 0,
36+
},
37+
{
38+
x: []float64{0, 0.5 * math.Pi},
39+
ans: 0.78539816339744,
40+
},
41+
{
42+
x: []float64{-1.5 * math.Pi, 0.5 * math.Pi, 2.5 * math.Pi},
43+
wts: []float64{1, 2, 3},
44+
ans: 0.5 * math.Pi,
45+
},
46+
{
47+
x: []float64{0, 0.5 * math.Pi},
48+
wts: []float64{1, 2},
49+
ans: 1.10714871779409,
50+
},
51+
} {
52+
c := CircularMean(test.x, test.wts)
53+
if math.Abs(c-test.ans) > 1e-14 {
54+
t.Errorf("Circular mean mismatch case %d: Expected %v, Found %v", i, test.ans, c)
55+
}
56+
}
57+
if !Panics(func() { CircularMean(make([]float64, 3), make([]float64, 2)) }) {
58+
t.Errorf("CircularMean did not panic with x, wts length mismatch")
59+
}
60+
}
61+
1662
func ExampleCorrelation() {
1763
x := []float64{8, -3, 7, 8, -4}
1864
y := []float64{10, 5, 6, 3, -1}

0 commit comments

Comments
 (0)